20

我需要可靠地重定向应用程序查找特定 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 读取和处理,但只是被忽略。

有人有什么想法吗?

谢谢!

托比

4

3 回答 3

22

因此,使用清单将调用重定向到具有绝对路径的 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"),是的,格式错误的路径...

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

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

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

于 2010-01-26T04:04:12.610 回答
8

好的,您需要像这样设置它:

  • 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 文件夹的应用程序文件夹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 引用它的过程:- 从中央存储库加载 DLL 的方法


只是为了澄清:一个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 位于同一文件夹中,并命名为:

<exename>.exe.config, 或者<dllname>.dll.2.config

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

于 2010-01-20T16:31:59.760 回答
0

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

于 2010-01-21T15:47:28.293 回答