Перенаправление DLL с использованием манифестов - PullRequest
17 голосов
/ 20 января 2010

Мне нужно надежно перенаправить поиск приложений конкретной библиотеки 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>

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

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

Спасибо!

Toby

Ответы [ 3 ]

18 голосов
/ 26 января 2010

Так что кажется невозможным перенаправить вызовы в 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 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 вашему пустышке. Fun.

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

8 голосов
/ 20 января 2010

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

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

В папке приложения 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, идентификатор опущен, но любое другое число означает, что оно становится частью имени файла).

0 голосов
/ 21 января 2010

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

...