使用清单的DLL重定向

时间:2010-01-20 11:30:28

标签: c++ windows dll manifest

我需要可靠地重定向应用程序查找特定的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:\)的Application文件夹包含带有嵌入式清单的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读取和处理,但只是被忽略。

有人有任何想法吗?

谢谢!

托比

3 个答案:

答案 0 :(得分:18)

因此,使用清单将绝对路径重定向到LoadLibrary似乎是不可能的。

在经历了大量的清单之后,似乎一旦你过去所有糟糕的文档清单实际上都是非常简单的。

基本上,当加载可执行文件时,windows会收集使用identity和dependency元素链接的所有相关清单。然后,对于清单文件中包含的每个文件元素,它会在激活上下文中添加一个条目:

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

现在,在进行加载库调用时,它会在激活上下文映射中搜索与加载库的路径参数匹配的键,然后使用该键的值执行loadlibrary。

因此,如果我的应用程序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 Trojaning方法。基本上,您创建了一个虚拟kernel32.dll,它将所有对原始kenerl32.dll的调用重定向,但LoadLibrary调用除外,您可以在其中放置自己的重定向逻辑。然后在应用程序清单中,放置一个文件元素,将kernel32.dll重定向到您的虚拟对象。乐趣。

所有这些都描述了我在Windows Xp Sp2上的实验。为了获得额外的乐趣,我认为几乎每个版本的Windows都会表现出不同的表现形式。

答案 1 :(得分: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”。 / p>

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

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

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

如果要将“emptyassm”清单和dll放在应用程序文件夹的其他文件夹中,并且如果您要定位Windows Server 2008或Windows 7或更高版本,则可以为您添加配置文件app: -

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

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


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


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

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

5.1之前的Windows版本(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被省略,但任何其他数字表示它成为文件名的一部分。)

答案 2 :(得分:0)

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