Перенаправление DLL с использованием манифестов

Мне нужно надежно перенаправить поиск приложений конкретной библиотеки DLL. Использование подхода app.exe.local не работает, поскольку локальные файлы игнорируются, если приложение имеет манифест (встроенный или отдельный файл). Поэтому я пытаюсь сделать перенаправление DLL, определяя DLL как частную сборку в манифестах.

У меня есть тестовое приложение, LoadDll.exe, которое просто вызывает

LoadLibrary("C:\\EmptyDll.dll");

LoadDll.exe имеет манифест (в виде отдельного файла, LoadDll.exe.manifest)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
  version="1.0.0.1"
  processorArchitecture="x86"
  name="LoadDll"
  type="win32"
/>
<dependency>
  <dependentAssembly>
    <assemblyIdentity
      type="win32"
      name="EmptyDll"
      version="1.0.0.1"
      processorArchitecture="x86"
    />
  </dependentAssembly>
</dependency>
</assembly>

Папка приложения, содержащая LoadDll.exe (НЕ c:\), содержит файл EmptyDll.dll со встроенным манифестом.

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<assemblyIdentity
      type="win32"
      name="EmptyDll"
   version="1.0.0.1"
      processorArchitecture="x86"
    />    
</assembly>

Однако LoadDll.exe запускается и загружает C:\EmptyDll.dll, а не EmptyDll.dll в папку приложения.

Если вы нарушаете любой манифест (например, изменяете номер версии в удостоверении манифеста EmptyDll.dll), LoadDll.exe не загружается, поэтому файлы манифеста читаются и обрабатываются окнами, а просто игнорируются.

У кого-нибудь есть идеи?

Спасибо!

Тоби

3 ответа

Так что кажется невозможным перенаправить вызовы в LoadLibrary с абсолютными путями, используя манифесты.

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

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

'name attribute of file element' -> 'absolute path of manifest file' + 'name attribute of file element'

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

Поэтому, если мое приложение c: \ foo \ foo.exe имеет зависимость от манифеста в c: \ foo \ baa \ baa.manifest, а baa.manifest содержит элемент file <file name="empty.dll"/>тогда контекст активации будет иметь отображение: "empty.dll" -> "c:\foo\baa\empty.dll"

Так что любые звонки LoadLibrary("empty.dll") будет перенаправлен на LoadLibrary("C:\foo\baa\empty.dll"),

Тем не мение, LoadLibrary("c:\anotherpath\empty.dll") Не будет перенаправлен!

Теперь, чтобы доказать мою мысль о том, насколько глупы простые файлы манифеста и контексты активации. Если элемент файла baa.manifest был <file name="c:\anotherpath\empty.dll"/> и ты сделал LoadLibrary("C:\anotherpath\empty.dll") вызов, вызов LoadLibrary будет перенаправлен на LoadLibrary("C:\foo\baa\C:\anotherpath\empty.dll")да, неправильный путь...

Элемент file имеет недокументированный атрибут "loadFrom", который выглядит так, как будто он идеально подходит для решения этой проблемы. Используя loadFrom, я смог перенаправить вызов loadlibrary с абсолютным путем, но, похоже, он странным образом испортил другие зависимости в исполняемом файле. Если кто-то знает больше о том, как работает loadFrom, я был бы очень заинтересован.

Так как же я решил свою проблему в конце? Используя невероятно сложный подход DLL Trojaning, описанный в Ethical Hacker. По сути, вы создаете фиктивную kernel32.dll, которая перенаправляет все вызовы на исходный файл kenerl32.dll, кроме вызовов LoadLibrary, в которые вы помещаете свою собственную логику перенаправления. Затем в манифесте приложения вы помещаете элемент файла, который перенаправляет kernel32.dll вашему пустышке. Веселье.

Все это описывает мои эксперименты на Windows Xp Sp2. Для дополнительного удовольствия я считаю, что манифесты ведут себя по-разному практически в каждой версии Windows.

Хорошо, вам нужно настроить это так:

  • c:\apppath\testapp.exe - exe-файл ваших тестовых приложений
  • c:\apppath\testapp.exe.manifest - потенциально встроенный файл манифеста приложения
  • c:\apppath\EmptyAssm\EmptyAssm.manifest - Манифест, описывающий вашу новую сборку.
  • c:\apppath\EmptyAssm\empty.dll - сборка длл
  • c:\apppath\EmptyAssm\empty.dll.2.manifest - встроенный манифест в dll

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

В подпапке приложения папка assm папки приложения у вас есть манифест сборки сборки "EmptyAssm", который содержит файловый узел, ссылающийся на фактическую dll, "empty.dll".

empty.dll встраивает свой собственный манифест, содержащий ссылки на зависимые сборки на любых открытых или закрытых сборках, которые ему требуются.

Это важный момент: манифест сборки "EmptyAssm" и "пустые" манифесты dll потенциально различны. Файл манифеста сборки ("EmptyAssm") НЕ ДОЛЖЕН быть внедрен, но может иметь общее имя манифеста dll, если вы решите назвать свой манифест именем dll.

Теперь, когда загрузчик загружает ваш EXE-файл, он загружает манифест вашего EXE-файла и добавляет его в контекст активации. Когда таблица импорта EXE обрабатывается, ИЛИ вы вызываете LoadLibrary, загрузчик сначала ищет в контексте активации манифест сборки с соответствующим файловым узлом. Если он находит соответствующую сборку, ТОГДА он обрабатывает и загружает dll из местоположения сборки (папка, содержащая сборки.manifest), и он может в это время, если в dll нет встроенного манифеста, а dll и манифест имеют то же имя, повторно использовать тот же файл манифеста, чтобы настроить контекст активации DLL.

Если вы хотите поместить манифест "emptyassm" и dll в другую папку, в папку приложения, и если вы нацелены на Windows Server 2008, Windows 7 или более позднюю версию, вы можете добавить файл конфигурации для своего приложения:-

  • c:\apppath\testapp.exe.config - файл конфигурации приложения

Файл конфигурации приложения может содержать пробный узел под assemblyBinding узел (файлы конфигурации очень похожи на файлы манифеста), с privatePath="some relative path", В этом случае в соответствующей папке будет произведен поиск сборок.


Мой последний ответ здесь содержит примеры файлов, описывающих процесс создания сборки из dll и ссылки на нее из exe:- Способ загрузки DLL из центрального репозитория


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

Сборки могут быть общими - путем предоставления им надежной версии (и некоторой цифровой подписи) и установки их в Windows\WinSxS или в частной.

Версии Windows до 5.1 (Win XP) вообще не будут искать сборки, так как эта технология была добавлена ​​только в XP. Windows 5.1 - 6.0 (XP и Vista) будут искать только частные сборки в папке объекта с активным контекстом активации:- Если exe ссылается на сборку, то папка, содержащая exe. Если код в dll ссылается на сборку, то выполняется поиск в папке dll.

Если вы хотите хранить свою dll в приватном месте, которое используется несколькими приложениями (например), вы ДОЛЖНЫ иметь требование Windows 7 или более поздней версии:-

Windows версии 6.1 (также известная как Windows Server 2008 или Windows 7) и более поздние версии, помимо папки модуля, будут искать путь, указанный в качестве элемента privatePath элемента исследования в файле конфигурации приложения. Файлы конфигурации приложения всегда находятся в той же папке, что и exe или dll, и имеют имя:

<exename>.exe.config, или же <dllname>.dll.2.config

(Причина.2. В том, что потенциально много встроенных манифестов и конфигураций в виде ресурсов, и загрузчик резервирует идентификаторы ресурсов 1...15. При поиске на диске манифеста файла конфигурации, если идентификатор ресурса встроенный ресурс был бы равен 1, идентификатор опущен, но любое другое число означает, что оно становится частью имени файла).

Вы можете обойти это, используя Detours, упаковку LoadLibrary, Оболочка LoadLibrary сможет распознавать попытки загрузить вашу DLL и переписать путь соответствующим образом.

Другие вопросы по тегам