【问题标题】:DLL redirection using manifests使用清单的 DLL 重定向
【发布时间】:2010-01-20 11:30:27
【问题描述】:

我需要可靠地重定向应用程序查找特定 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 不会加载,因此清单文件正在被 Windows 读取和处理,但只是被忽略。

有人有什么想法吗?

谢谢!

托比

【问题讨论】:

  • 您使用绝对路径是否有原因?
  • 不幸的是。即使没有,重新编译 exe 也不是一种选择。

标签: c++ windows dll manifest


【解决方案1】:

因此,使用清单将调用重定向到具有绝对路径的 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 包含一个文件元素 &lt;file name="empty.dll"/&gt;,那么激活上下文将有一个映射:"empty.dll" -&gt; "c:\foo\baa\empty.dll"

因此,任何对LoadLibrary("empty.dll") 的调用都将被重定向到LoadLibrary("C:\foo\baa\empty.dll")

但是,LoadLibrary("c:\anotherpath\empty.dll") 不会被重定向!

现在来证明我的观点,即清单文件和激活上下文是多么简单。如果 baa.manifest 的文件元素是 &lt;file name="c:\anotherpath\empty.dll"/&gt; 并且您进行了 LoadLibrary("C:\anotherpath\empty.dll") 调用,则 LoadLibrary 调用将被重定向到 LoadLibrary("C:\foo\baa\C:\anotherpath\empty.dll"),是的,路径格式错误...

文件元素确实有一个名为“loadFrom”的未记录属性,它的功能听起来很像,并且似乎完美地解决了这个问题。使用 loadFrom,我能够重定向绝对路径 loadlibrary 调用,但它似乎以奇怪的方式搞砸了可执行文件中的其他依赖项。如果有人更了解“loadFrom”的工作原理,我会非常感兴趣。

那我最后是怎么解决我的问题的呢?通过使用在Ethical Hacker 中描述的极其繁琐的 DLL 木马方法。基本上,您创建了一个虚拟 kernel32.dll,它将所有调用重定向到原始 kenerl32.dll,LoadLibrary 调用除外,您可以在其中放置自己的重定向逻辑。然后在应用程序清单中,放置一个将 kernel32.dll 重定向到虚拟对象的文件元素。有趣。

所有这些都描述了我在 Windows Xp Sp2 上的实验。为了获得更多乐趣,我相信清单在几乎每个版本的 Windows 上的行为都不同。

【讨论】:

  • XP 似乎需要相对于启动应用程序的清单“名称”。 Vista+ 需要相对于清单位置的“名称”。
  • 如果 Microsoft Office 决定添加“empty.dll”,您是否也希望替换它?裸 DLL 名称不是全局唯一的。需要该路径以使其至少在本地唯一。
  • 我在尝试重定向 DLL 加载时也遇到了麻烦。您了解在一般情况下如何使用 loadFrom 吗?
【解决方案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 程序集的一个。

在 app 文件夹的 application 文件夹 assm 子文件夹中,您有“EmptyAssm”程序集的程序集清单,其中包含引用实际 dll 的文件节点“empty.dll”。

empty.dll 嵌入它自己的清单,包含对它所需的任何公共或私有程序集的依赖程序集引用。

这是重点:“EmptyAssm”程序集清单和“空”dll 清单可能不同。 ("EmptyAssm") 程序集的清单文件不得嵌入,但如果您选择以 dll 的名称命名清单,则可能会共享 dll 清单名称。

现在,当加载程序加载您的 EXE 时,它会加载您的 EXE 清单并将其添加到激活上下文中。当处理 EXE 的导入表或调用 LoadLibrary 时,加载程序首先在激活上下文中搜索具有匹配文件节点的程序集清单。如果找到匹配的程序集,则它会从程序集位置(包含程序集 .manifest 的文件夹)处理并加载 dll,此时,如果 dll 中没有嵌入式清单并且 dll 和清单有相同的名称,重复使用相同的清单文件来设置 dll 的激活上下文。

如果您想将“emptyassm”清单和 dll 放在与您的应用程序文件夹不同的文件夹中,并且如果您的目标是 Windows Server 2008、Windows 7 或更高版本,您可以为您的应用程序:-

  • c:\apppath\testapp.exe.config - 应用配置文件

应用配置文件可以在assemblyBinding 节点下包含一个探测节点(配置文件看起来很像清单文件),带有privatePath="some relative path"。在这种情况下,将在相关文件夹中搜索程序集。


我在这里的最后一个回复有示例文件,涵盖了从 dll 创建程序集并从 exe 引用它的过程:- A way to load DLL from central repository


只是为了澄清: win32 程序集是(最简单的)描述程序集的清单文件和 dll。 在此模型中,它们始终位于同一文件夹中,因此清单的文件节点根本不能包含任何路径信息 - 只有 dll 的名称。

可以共享程序集 - 通过为它们提供强大的版本(和一些数字签名)并将它们安装在 Windows\WinSxS 或私有中。

5.1 (Win XP) 之前的 Windows 版本根本不会搜索程序集,因为该技术仅在 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 位于同一文件夹中,并命名为:

&lt;exename&gt;.exe.config,或&lt;dllname&gt;.dll.2.config

(.2. 的原因是可能有很多清单和配置作为资源嵌入,并且加载程序保留资源 id 的 1...15。在磁盘中搜索配置文件清单时,如果资源嵌入资源的 id 应该是 1,id 被省略,但任何其他数字都意味着它成为文件名的一部分。

【讨论】:

  • 您好,感谢您的详细回复。我已经尝试过您概述的内容,但结果相同。然而,使用进程监视器,我注意到 LoadLibrary 调用仅受清单影响,如果文件元素的 name 属性与 loadlibrary 参数完全匹配。因此,如果文件元素设置为 那么 loadlibrary 调用将尝试加载 "c:\apppath\\C:\EmptyDll.dll" 这显然不起作用。
  • 您不能在文件节点元素中使用重定向。它不 - 正如你所注意到的 - 工作。程序集必须始终位于单个包中:- dll 文件和程序集清单位于同一文件夹中。 Windows 7 之前的 Windows 版本将仅在当前文件夹(或当前文件夹的具有程序集名称的子文件夹)中搜索程序集。 Windows 7 及更高版本将检查应用程序配置文件中的“探测”节点,该节点可以指定要搜索的备用位置 - 可以包含相对路径。
【解决方案3】:

您可以使用Detours 包装LoadLibrary 来解决此问题。 您的 LoadLibrary 包装器将能够识别加载 DLL 的尝试并适当地重写路径。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    • 2016-05-31
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-14
    相关资源
    最近更新 更多