Как получить dll из загрузчика

Обновлено: 21.11.2024

Во время некоторых заданий RedTeam нам приходится писать библиотеки DLL. Однако отладка DLL не так проста, как кажется, поскольку DLL не предназначена для самостоятельной работы.
В этой статье мы рассмотрим, как можно эффективно отлаживать DLL.

Что такое DLL?

DLL — это сокращение от динамически подключаемой библиотеки. Это модульный подход к написанию кода. Библиотеки DLL написаны на C или C++ и при нормальных обстоятельствах служат библиотекой, к которой программы могут обращаться для выполнения функций. В какой-то степени вы можете сравнить DLL с пакетом python или пакетом npm, с любой библиотекой. Следующий рисунок довольно хорошо иллюстрирует концепцию DLL и EXE:

Как уже упоминалось в преамбуле, библиотека DLL не предназначена для выполнения сама по себе, в результате чего обычный способ отладки не может быть реализован "из коробки".

Способ отладки 1: использование OutputDebugString

У Microsoft есть функция в kernel32.dll (о, смотрите, DLL!) под названием OutputDebugString, которая выводит содержимое строки в системный отладчик. Visual Studio имеет встроенные возможности для получения этих выходных данных (подробнее об этом позже). Я предпочитаю системный отладчик Dbgview.exe из пакета sysinternals. Чтобы проиллюстрировать, как работает OutputDebugString, я подготовил небольшой демонстрационный код:

В качестве доказательства того, что DLL не может запускаться сама по себе, я пытаюсь запустить DemoDLL с помощью кнопки запуска в Visual Studio, и меня встречает этот маленький человечек:

DLL не работают сами по себе…

К счастью, у Microsoft есть встроенный загрузчик DLL с именем rundllll32.exe, но rundll32.exe может раздражать, если в вашей DLL нет экспортированных функций (= функций, которые будут использоваться другими программами). Мой коллега Дидье Стивенс довольно подробно объясняет точки входа в DLL в своем блоге. Если вам интересно узнать больше об этом, пожалуйста, проверьте это!

Как вы можете видеть в демонстрационном коде, все мои выходные строки отладки содержат «[DBG]», это было сделано не без причины.
Dbgview — это системный отладчик, который будет производить много шума в зависимости от того, что вы установили в системе. Однако в Dbgview есть параметры фильтрации. Поскольку все строки отладки содержат «[DBG]», мы можем указать Dbgview показывать нам только эти сообщения следующим образом:

Фильтр DebugViewer для отображения только строк, содержащих [DBG]

Все настроено, пора запустить нашу DLL и посмотреть на вывод DebugView:

Результат применения фильтра и запуска DLL

Как и ожидалось, выходные данные DebugView отлично показывают наши отладочные строки!

Способ отладки 2: использование Printf

Этот метод использует printf вместо OutputDebugString, поэтому давайте адаптируем наш код:

Используя загрузчик DLL, мы увидим, что все выходные данные printf записываются в стандартный вывод программы-загрузчика DLL, которым в данном случае является окно консоли.

Демонстрация программы-загрузчика DLL

Способ отладки 3. Использование загрузчика DLL для отладки в Visual Studio

Теперь, когда у нас есть суррогатный механизм загрузки DLL, мы можем указать Visual Studio использовать его в качестве родительского процесса, таким образом, мы можем запускать DLL в среде отладки Visual Studio, и мы сможем отлаживать DLL так же, как вы сможете отлаживать EXE-файлы.

Перейдите к свойствам проекта и выберите вкладку "Отладка"

Как открыть окно свойств отладки в Visual Studio

В поле Команда указываем путь к исполняемому файлу загрузчика DLL, в поле Аргументы команды указываем аргументы для загрузчика DLL, в данном случае путь к отладочной демонстрационной DLL, так как у нас нет экспортируемой функции , мы не используем второй аргумент. Если вы хотите использовать второй аргумент, вы можете это сделать, как в консоли.

Окно свойств отладки в Visual Studio

Теперь мы можем настроить несколько точек останова в Visual Studio:

Пример установки точек останова в Visual Studio

Запуск библиотеки DLL теперь можно выполнить с помощью зеленой кнопки воспроизведения в самой Visual Studio. Это даст указание загрузчику DLL начать работу с аргументами, указанными на странице свойств проекта.
Как видите, теперь Visual Studio правильно нажимает и обрабатывает точки останова, как обычно:

Показаны точки останова, когда загрузчик DLL используется в качестве суррогатной программы

Поскольку операторы printf все еще присутствуют, они также будут выводиться в консоль:

Отображение журнала вывода printf на консоль благодаря загрузчику DLL во время отладки в Visual Studio

Заключение

В зависимости от сложности вашего кода DLL в большинстве случаев вы сможете обойтись без использования загрузчика DLL в Visual Studio, чтобы выполнять отладку, как отладку любой другой программы. При желании вы можете комбинировать загрузчик DLL с инструкциями printf, чтобы вы могли видеть обзор происходящего в режиме реального времени через консольный вывод загрузчика DLL.
Однако, если ваша DLL выполняет сложные операции, такие как создание новых потоков или выполнение асинхронных вызовов, лучше использовать OutputDebugString, так как загрузчик DLL не будет принимать во внимание новые потоки или асинхронные вызовы.

На этом наше путешествие в мир отладки библиотек DLL завершено. Я надеюсь, что эта запись в блоге была поучительной и научила вас чему-то полезному!

Об авторе

Я хочу прочитать двоичный файл DLL-загрузчика моей DLL. Но есть exe и 2 DLL, которые я использую первый (X DLL) для загрузки второго (Y DLL). Когда я загружаю Y dll из X DLL через exe, GetModuleFileNameA(NULL, szEXEPath, 2048); функция дает мне только путь к exe. Я хочу получить путь X DLL. Я мог бы использовать GetModuleFileNameA("X DLL Name", szEXEPath, 2048); но я не знаю имени X DLL.

Когда я читал справку MSDN для GetModuleFileName, я увидел ниже описание первого параметра функции.

Дескриптор загруженного модуля, путь к которому запрашивается. Если этот параметр имеет значение NULL , GetModuleFileName получает путь к исполняемому файлу текущего процесса.

Я не хочу получать путь к исполняемому файлу, я просто хочу определить, какая DLL загружает мою текущую DLL. Есть ли способ найти DLL пути загрузчика?

Я понимаю, что это означает, что вам нужно имя "родительской" dll, которая загружает вашу dll (см. ниже). С некоторой помощью это не так уж сложно.

@DavidHeffernan X уже вызывает некоторые функции в Y. Но мне не удалось получить путь или имя X в Y.

Только потому, что ты не сделал того, что я сказал. Y нужно экспортировать функцию, которая принимает необходимую информацию. Затем X вызывает эту функцию, передавая эту информацию.

2 ответа 2

Чтобы получить имя "родительской" dll, вы можете создать функцию как часть API, который требуется вызывать и передать в дескрипторе HMODULE родительской dll.

Создание приведенной ниже функции get_current_module_handle() как встроенной и включение ее в какой-либо макрос упростит процесс для клиентского кода.

Чтобы получить имя dll (в дочернем элементе), вы можете использовать комбинацию функций GetModuleFileNameEx и GetModuleHandleEx. Хитрость заключается в функции GetModuleHandleEx, которая позволяет получить дескриптор модуля через указатель на функцию (то есть функцию в dll); особенно использование флага GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS .

Далее следует фрагмент кода, который должен сработать;

Примечание. Выше воспроизведена упрощенная версия (на основе длины 2048 в ОП). Также представлена ​​рекурсивная реализация для учета длинных имен файлов (проверьте правки).

Имя dll будет полным путем к модулю, поэтому для получения базового имени файла используются такие функции, как _splitpath . Кроме того, вы можете использовать GetModuleBaseName непосредственно в приведенном выше коде.

Легко загружайте тщательно отобранные списки модов, наша функция «Коллекции» перешла в режим открытого альфа-тестирования. Посетите нашу вики, чтобы начать.

  • Все игры
  • Специальное издание Skyrim
  • Моды
  • Утилиты
  • Загрузчик плагинов DLL

Информация о файле

Последнее обновление

Исходная загрузка

Создатель

Загружено

Сканирование на вирусы

Теги для этого мода

Об этом моде

Простой загрузчик подключаемых модулей DLL.

Этот мод не имеет никаких известных зависимостей, кроме базовой игры.

Моды, требующие этот файл

Кредиты и разрешение на распространение

  • Другие ресурсы пользователя Все ресурсы в этом файле принадлежат автору или взяты из бесплатных ресурсов моддеров
  • Разрешение на загрузку. Вы не можете загружать этот файл на другие сайты ни при каких обстоятельствах.
  • Разрешение на изменение Вы должны получить от меня разрешение, прежде чем вам будет разрешено изменять мои файлы для их улучшения.
  • Разрешение на преобразование. Вам не разрешено преобразовывать этот файл для работы в других играх ни при каких обстоятельствах.
  • Разрешение на использование объекта Вы должны получить от меня разрешение, прежде чем вам будет разрешено использовать любой из ресурсов в этом файле
  • Разрешение на использование активов в продаваемых модах/файлах. Вам не разрешено использовать активы из этого файла в каких-либо модах/файлах, которые продаются за деньги в Steam Workshop или на других платформах
  • Разрешение на использование ресурсов в модах/файлах, которые зарабатывают очки пожертвований. Вы должны получить разрешение на получение очков пожертвований для ваших модов, если они используют мои активы.

Примечания автора

Этот автор не предоставил никаких дополнительных примечаний относительно прав доступа к файлам

Источники файлов

Этот автор не указал никого другого в этом файле

Система баллов пожертвований

Этот мод не позволяет получать очки пожертвований

Как установить:

<р>1. Перейдите в корневой каталог Skyrim Special Edition. Это та же папка, где находится SkyrimSE.exe, обычно "C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\"

<р>2. Переименуйте «binkw64.dll» в «binkw64_.dll». <р>3. Поместите «binkw64.dll» из скачанного архива в папку, указанную в шаге 1.

<р>4. Если вы все сделали правильно, то теперь у вас в папке должно быть два файла: "binkw64.dll" и "binkw64_.dll", вот и все, теперь можно начинать игру.


Как проверить, все ли работает правильно:

<р>1. Перейдите туда, где вы установили «binkw64.dll» (папка SkyrimSE.exe)

<р>2. Создайте новый файл, если он не существует, с именем «binkw64.log», убедитесь, что в расширении указано «log», а не «txt»!

<р>3. Запустите игру, а затем выйдите из игры, это нормально, если вы вошли только в главное меню, не нужно загружать какие-либо сохранения.

<р>4. Теперь откройте файл "binkw64.log", в нем будет указано, какие плагины были проверены и правильно ли они загружены.

<р>5. Если файл пустой, то плагины не найдены! Это может указывать на проблему с разрешениями или на то, что путь к папке «Data\DLLPlugins» не существует.


Для авторов модов - как загрузить вашу DLL:

<р>1. Поместите файл dll в "Data\DLLPlugins". Вот и все, ваш плагин будет загружен автоматически.

Эта функция будет вызываться сразу после DllMain, но это не требуется, если вы выполняете только простые изменения памяти.


Зачем делать еще один, когда другие уже существуют?

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

Извлечь Dll из LOADeR

Можно ли извлечь dll из примера загрузчика.. **** загрузчик, я хочу dll для него, но не знаю, если это возможно, помогите мне, пожалуйста, я нуб

/me я так, как правда и свет боятся меня
/yea


SNIPPADAN
мое золото должно быть профессиональным хакером прогресс до сих пор
0-100% мне нужна помощь
[YOUTUBE]Combat Arms: Clan War -- Hacking Clan -- ~Grave Diggaz~[/YOUTUBE]
ВСЕ ЛОЖЬ, МОЙ КЛАНЕ НЕ ВЗЛОМАЕТ

Дата регистрации июль 2009 г. Пол Сообщений 12 528 Репутация 981 Спасибо 1 884 --> Спасибо 10 409 Мое настроение

/me я так, как правда и свет боятся меня
/yea


SNIPPADAN
мое золото должно быть профессиональным хакером прогресс до сих пор
0-100% мне нужна помощь
[YOUTUBE]Combat Arms: Clan War -- Hacking Clan -- ~Grave Diggaz~[/YOUTUBE]
ВСЕ ЛОЖЬ, МОЙ КЛАНЕ НЕ ВЗЛОМАЕТ

Дата регистрации июль 2011 г. Пол Местоположение Bat Gang Сообщений 1,977 Репутация 239 Спасибо 13 --> Спасибо 208 My Mood

Дата регистрации: февраль 2011 г. Пол Сообщения 104 Репутация 36 Спасибо 44 --> Спасибо 13

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

Подписать Poopbear

Что? Думаешь, я классный? Докажите это. Нажмите ту кнопку, которая начинается с буквы "Т" и заканчивается на "мэнкс".

ТЫ ЗНАЕШЬ, ЧТО ХОЧЕШЬ.

Дата регистрации апрель 2010 г. Пол Сообщения 3843 Репутация 425 Спасибо 44616 --> Спасибо 8615

Дата регистрации Сентябрь 2009 г. Пол Местоположение Кекистан Сообщений 15 558 Репутация 1 817 Спасибо 3 269 --> Спасибо 6 660

Первоначальное сообщение от flamesvor10

Первоначальное сообщение от NOOB

одна из этих двух вещей

Похожие темы

[Справка] Как извлечь исходный код dll?
Загрузчик DLL
Упаковка файлов .Dll в Loader.exe

Ресурсы сохранены на этой странице: MySQL 16,67% Copyright � 2001-2021. MPGH. Все права защищены.
Нравится MPGH? Пожертвовать

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

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

Неправильная синхронизация в DllMain может привести к взаимоблокировке приложения или доступу к данным или коду в неинициализированной библиотеке DLL. Такие проблемы возникают при вызове определенных функций из DllMain.

Общие рекомендации

DllMain вызывается, пока удерживается блокировка загрузчика. Поэтому на функции, которые можно вызывать из DllMain, накладываются существенные ограничения. Таким образом, DllMain предназначен для выполнения минимальных задач инициализации с использованием небольшого подмножества API Microsoft® Windows®. Вы не можете вызвать какую-либо функцию в DllMain, которая прямо или косвенно пытается получить блокировку загрузчика. В противном случае вы можете привести к взаимоблокировке или сбою вашего приложения. Ошибка в реализации DllMain может поставить под угрозу весь процесс и все его потоки.

Идеальный DllMain был бы просто пустой заглушкой. Однако, учитывая сложность многих приложений, это, как правило, слишком ограничительно. Хорошее эмпирическое правило для DllMain — как можно дольше откладывать инициализацию. Отложенная инициализация повышает надежность приложения, поскольку эта инициализация не выполняется, пока удерживается блокировка загрузчика. Кроме того, отложенная инициализация позволяет безопасно использовать гораздо больше возможностей Windows API.

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

Вы никогда не должны выполнять следующие задачи из DllMain:

  • Вызов LoadLibrary или LoadLibraryEx (прямо или косвенно). Это может привести к взаимоблокировке или сбою.
  • Вызывать GetStringTypeA, GetStringTypeEx или GetStringTypeW (прямо или косвенно). Это может привести к взаимоблокировке или сбою.
  • Синхронизировать с другими потоками. Это может привести к взаимоблокировке.
  • Получить объект синхронизации, принадлежащий коду, ожидающему получения блокировки загрузчика. Это может привести к взаимоблокировке.
  • Инициализируйте потоки COM с помощью CoInitializeEx. При определенных условиях эта функция может вызывать LoadLibraryEx.
  • Вызовите функции реестра. Эти функции реализованы в Advapi32.dll. Если Advapi32.dll не инициализируется до вашей DLL, DLL может получить доступ к неинициализированной памяти и вызвать сбой процесса.
  • Вызовите CreateProcess. Создание процесса может загрузить другую DLL.
  • Вызов ExitThread. Выход из потока во время отсоединения DLL может привести к повторному захвату блокировки загрузчика, что приведет к взаимоблокировке или сбою.
  • Вызов CreateThread. Создание потока может работать, если вы не синхронизируете его с другими потоками, но это рискованно.
  • Создайте именованный канал или другой именованный объект (только для Windows 2000). В Windows 2000 именованные объекты предоставляются библиотекой служб терминалов. Если эта библиотека DLL не инициализирована, вызовы библиотеки DLL могут привести к сбою процесса.
  • Используйте функцию управления памятью из динамической среды выполнения C (CRT). Если CRT DLL не инициализирована, вызовы этих функций могут привести к сбою процесса.
  • Вызов функций в User32.dll или Gdi32.dll. Некоторые функции загружают другую DLL, которая может быть не инициализирована.
  • Используйте управляемый код.

В DllMain безопасно выполнять следующие задачи:

  • Инициализировать статические структуры данных и члены во время компиляции.
  • Создайте и инициализируйте объекты синхронизации.
  • Выделить память и инициализировать динамические структуры данных (избегая функций, перечисленных выше).
  • Настройте локальное хранилище потоков (TLS).
  • Открывать, читать и записывать в файлы.
  • Вызов функций в Kernel32.dll (кроме функций, перечисленных выше).
  • Установите глобальные указатели в NULL, отложив инициализацию динамических элементов. В Microsoft Windows Vista™ вы можете использовать функции однократной инициализации, чтобы убедиться, что блок кода выполняется только один раз в многопоточной среде.

Взаимоблокировки, вызванные инверсией порядка блокировки

При реализации кода, использующего несколько объектов синхронизации, таких как блокировки, очень важно соблюдать порядок блокировок. Когда необходимо получить более одной блокировки одновременно, вы должны определить явный приоритет, который называется иерархией блокировок или порядком блокировок. Например, если блокировка A устанавливается перед блокировкой B где-то в коде, а блокировка B устанавливается перед блокировкой C где-то в коде, то порядок блокировок будет A, B, C, и этот порядок должен соблюдаться во всем коде. Инверсия порядка блокировки происходит, когда порядок блокировки не соблюдается, например, если блокировка B получена до блокировки A. Инверсия порядка блокировки может вызвать взаимоблокировки, которые трудно отладить. Чтобы избежать таких проблем, все потоки должны получать блокировки в одном и том же порядке.

Важно отметить, что загрузчик вызывает DllMain с уже полученной блокировкой загрузчика, поэтому блокировка загрузчика должна иметь наивысший приоритет в иерархии блокировок. Также обратите внимание, что код должен получить только те блокировки, которые ему необходимы для правильной синхронизации; ему не нужно получать каждую отдельную блокировку, определенную в иерархии. Например, если разделу кода для правильной синхронизации требуются только блокировки A и C, код должен получить блокировку A до того, как он получит блокировку C; нет необходимости, чтобы код также получал блокировку B. Кроме того, код DLL не может явно получить блокировку загрузчика. Если код должен вызвать API, такой как GetModuleFileName, который может косвенно получить блокировку загрузчика, и код должен также получить частную блокировку, тогда код должен вызвать GetModuleFileName до того, как он получит блокировку P, тем самым обеспечив соблюдение порядка загрузки.

На рис. 2 показан пример, иллюстрирующий инверсию порядка блокировки. Рассмотрим DLL, основной поток которой содержит DllMain. Загрузчик библиотеки получает блокировку загрузчика L и затем вызывает DllMain. Основной поток создает объекты синхронизации A, B и G для сериализации доступа к своим структурам данных, а затем пытается получить блокировку G. Рабочий поток, который уже успешно получил блокировку G, затем вызывает функцию, такую ​​как GetModuleHandle, которая пытается получить загрузчик lock L. Таким образом, рабочий поток блокируется на L, а основной поток блокируется на G, что приводит к взаимоблокировке.

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

Рекомендации по синхронизации

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

Синхронизация потоков в DllMain во время выхода из процесса

  • К моменту вызова DllMain при выходе из процесса все потоки процесса были принудительно очищены, и существует вероятность того, что адресное пространство несовместимо. Синхронизация в этом случае не требуется. Другими словами, идеальный обработчик DLL_PROCESS_DETACH пуст.
  • Windows Vista гарантирует, что основные структуры данных (переменные среды, текущий каталог, куча процессов и т. д.) находятся в согласованном состоянии. Однако другие структуры данных могут быть повреждены, поэтому очистка памяти небезопасна.
  • Постоянное состояние, которое необходимо сохранить, должно быть сброшено в постоянное хранилище.

Синхронизация потоков в DllMain для DLL_THREAD_DETACH во время выгрузки DLL

  • Когда DLL выгружается, адресное пространство не выбрасывается. Поэтому ожидается, что DLL выполнит чистое завершение работы. Это включает в себя синхронизацию потоков, открытые дескрипторы, постоянное состояние и выделенные ресурсы.
  • Синхронизация потоков сложна, поскольку ожидание завершения потоков в DllMain может привести к взаимоблокировке. Например, DLL A удерживает блокировку загрузчика. Он сигнализирует потоку T о выходе и ожидает выхода потока. Поток T завершается, и загрузчик пытается получить блокировку загрузчика, чтобы вызвать DllMain библиотеки DLL A с помощью DLL_THREAD_DETACH. Это вызывает взаимоблокировку. Чтобы свести к минимуму риск взаимоблокировки:
    • DLL A получает сообщение DLL_THREAD_DETACH в свою DllMain и устанавливает событие для потока T, сигнализируя ему о выходе.
    • Поток T завершает свою текущую задачу, приводит себя в согласованное состояние, передает сигнал DLL A и ждет бесконечно. Обратите внимание, что процедуры проверки непротиворечивости должны соответствовать тем же ограничениям, что и DllMain, чтобы избежать взаимной блокировки.
    • DLL A завершает T, зная, что он находится в согласованном состоянии.

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

    Читайте также: