Перенаправление 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(NOT 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 не загружается, поэтому файлы манифеста читаются и обрабатываются окнами, но просто игнорируются.

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

Спасибо!

Тоби

Ответ 1

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

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

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

'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 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.

Ответ 2

Хорошо, вам нужно настроить его следующим образом:

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

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

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

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

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

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

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

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

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


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


Просто уточнить: Узел win32 (в самом простейшем) - файл манифеста, описывающий сборку, и dll. Они всегда в этой модели находятся в одной папке, поэтому файл node манифеста вообще не может содержать никакой информации о пути - только имя 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. Есть потенциально много манифеста и конфигов, встроенных в качестве ресурсов, и загрузчик резервирует ресурсы id 1... 15. При поиске на диске для манифеста файла конфигурации, если ресурс id встроенного ресурса будет 1, id опущен, но любое другое число означает, что оно становится частью имени файла).

Ответ 3

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