使用清单的 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 的应用程序文件夹(不是 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 会读取和处理清单文件,但只是将其忽略。
有人有什么想法吗?
谢谢!
托比
I need to reliably redirect an applications look up of a specific DLL. Using the app.exe.local approach does not work because local files are ignored if the application has a manifest (embedded or separate file). So I am trying to do DLL redirection by defining the DLL as a private assembly in the manifests.
I have a test application, LoadDll.exe which simply calls
LoadLibrary("C:\\EmptyDll.dll");
The LoadDll.exe has the manifest (as a separate file, 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>
The Application folder containing LoadDll.exe (NOT c:\) contains the EmptyDll.dll with the embedded manifest.
<?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>
However, LoadDll.exe goes ahead and loads C:\EmptyDll.dll, and not the EmptyDll.dll in the application folder.
If you break either manifest (e.g. change the version number in the EmptyDll.dll manifest identity), LoadDll.exe does not load, so the manifest files are being read and processed by windows, but just ignored.
Anyone got any ideas?
Thanks!
Toby
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因此,似乎不可能使用清单将调用重定向到具有绝对路径的
LoadLibrary
。经过多次使用清单后,似乎一旦你克服了所有糟糕的文档清单,实际上就变得非常简单了。
基本上,当加载可执行文件时,Windows 会收集使用标识和依赖项元素链接的所有相关清单。然后,对于清单文件中包含的每个文件元素,它会在激活上下文中添加一个条目:
现在,当进行加载库调用时,它会在激活上下文映射中搜索与加载库的路径参数匹配的键,然后调用
Loadlibrary
以及该键的值。因此,如果我的应用程序
c:\foo\foo.exe
依赖于c:\foo\baa\baa.manifest
和baa.manifest 中的清单。 Manifest
包含文件元素
,则激活上下文将具有映射:"empty.dll" -> "c:\foo\baa\empty.dll"
因此,任何对
LoadLibrary("empty.dll")
的调用都将被重定向到LoadLibrary("C:\foo\ baa\empty.dll")
。但是,
LoadLibrary("c:\anotherpath\empty.dll")
不会被重定向!现在来证明我的观点:清单文件和激活上下文是多么简单。如果 baa.manifest 的文件元素为
并且您创建了LoadLibrary("C:\anotherpath\empty .dll")
调用,LoadLibrary 调用将被重定向到LoadLibrary("C:\foo\baa\C:\anotherpath\empty.dll")
,是的,格式错误的路径...file 元素确实有一个名为“loadFrom”的未记录属性,它的作用正如听起来的那样,并且似乎非常适合解决这个问题。使用 loadFrom,我能够重定向绝对路径 loadlibrary 调用,但它似乎以奇怪的方式搞砸了可执行文件中的其他依赖项。如果有人了解更多有关“loadFrom”如何工作的信息,我会非常感兴趣。
那么我的问题最后是怎么解决的呢?通过使用 道德黑客。基本上,您创建一个虚拟 kernel32.dll,它将所有调用重定向到原始 kenerl32.dll,但 LoadLibrary 调用除外,您可以在其中放置自己的重定向逻辑。然后在应用程序清单中,放置一个文件元素,将 kernel32.dll 重定向到您的虚拟文件。乐趣。
这一切描述了我在Windows Xp Sp2上的实验。为了获得额外的乐趣,我相信清单在几乎每个版本的 Windows 上都有不同的行为。
So it seems its impossible to redirect calls to
LoadLibrary
with absolute paths using manifests.After a lot of playing around with manifests, it seems that once you get past all the bad documentation manifests are actually stupidly simple.
Basically when the executable is loaded Windows collects all the related manifests that are linked using the identity and dependency elements. Then for each file element contained in the manifest files, it adds an entry into the activation context:
Now when a load library call is made, it searches the activation context map for a key that matches the path argument of load library, and then calls
Loadlibrary
with the value for that key.So if my application
c:\foo\foo.exe
has a dependency on the manifest inc:\foo\baa\baa.manifest
, andbaa.manifest
contains a file element<file name="empty.dll"/>
, then the activation context will have the mapping:"empty.dll" -> "c:\foo\baa\empty.dll"
So any calls to
LoadLibrary("empty.dll")
will be redirected toLoadLibrary("C:\foo\baa\empty.dll")
.However,
LoadLibrary("c:\anotherpath\empty.dll")
Will not be redirected!Now to prove my point of how stupidly simple manifest files and activation contexts are. If the file element of baa.manifest was
<file name="c:\anotherpath\empty.dll"/>
and you made aLoadLibrary("C:\anotherpath\empty.dll")
call, the LoadLibrary call will be redirected toLoadLibrary("C:\foo\baa\C:\anotherpath\empty.dll")
, yes, a malformed path...The file element does have an undocumented attribute called "loadFrom", which does what it sounds like, and seems like its perfect to solve this problem. Using loadFrom, I was able to redirect an absolute path loadlibrary call, but it seemed to screw up other dependencies in the executable in weird ways. If someone knows more about how "loadFrom" works I would be very interested.
So how did I solve my problem in the end? By using an incredibly heavy handed approach of DLL Trojaning described at Ethical Hacker. Basically you create a dummy kernel32.dll that redirects all calls to the original kenerl32.dll, except for the LoadLibrary calls, in which you place your own redirection logic. Then in the applications manifest, you place a file element that redirects the kernel32.dll to your dummy. Fun.
All this describes my experiments on Windows Xp Sp2. For extra fun I'm led to believe manifests behave differently on almost every version of Windows.
好的,您需要像这样设置:
c:\apppath\testapp.exe
- your test apps exe filec:\apppath\testapp.exe.manifest
- - 可能嵌入的应用程序清单文件c:\apppath\EmptyAssm\EmptyAssm.manifest
- 描述新程序集的清单。c:\apppath\EmptyAssm\empty.dll
- 程序集 dll,您的测试应用程序包含一个应用程序清单:其中包含应用程序的依赖程序集引用 - 包括您添加到自定义 dll 程序集中的引用。
在应用程序文件夹的
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
- 应用程序配置文件 应用程序配置文件可以在
assembleBinding
节点下包含一个探测节点(配置文件看起来很像清单文件) ),带有privatePath="somerelative path"
。在这种情况下,将在相对文件夹中搜索程序集。我在这里的最后一个回复有示例文件,涵盖了从 dll 创建程序集并从 exe 引用它的过程:-
一种从中央存储库加载 DLL 的方法
只是为了澄清一下:
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 位于同一文件夹中,并命名为:
.exe.config
或(.2. 的原因是可能存在大量作为资源嵌入的清单和配置,并且加载程序保留资源 id 的 1...15。在磁盘中搜索配置文件清单时,如果资源嵌入资源的 id 将为 1,该 id 被省略,但任何其他数字都意味着它成为文件名的一部分)。
Ok, you need to set it up like this:
c:\apppath\testapp.exe
- your test apps exe filec:\apppath\testapp.exe.manifest
- the - potentially embedded application manifest filec:\apppath\EmptyAssm\EmptyAssm.manifest
- A manifest describing your new assembly.c:\apppath\EmptyAssm\empty.dll
- the assembly dllc:\apppath\EmptyAssm\empty.dll.2.manifest
- the embedded manifest in the dllSo, you have your test application which contains an application manifest: which contains dependent assembly references for the app - including one you add to your custom dll assembly.
In the
application folderassm subfolder of the app folder you have the assembly-manifest of the "EmptyAssm" assembly, which contains a file node referencing the actual dll, "empty.dll".empty.dll embeds its own manifest, containing dependent assembly references on any public or private assemblies it requires.
This is the important point: the "EmptyAssm" assembly manifest, and the "empty" dll manifests are potentially different. The ("EmptyAssm") assembly's manifest file MUST NOT be embedded, but might share the dll manifest name IF you choose to name your manifest by the name of the dll.
Now, when the loader loads your EXE, it loads your EXE's manifest and adds it to the activation context. When the EXE's import table is processed, OR you call LoadLibrary, the loader first searches the activation context for an assembly manifest with a matching file node. If it finds a matching assembly, THEN it processess and loads the dll from the assembly location (the folder containing the assemblies .manifest) and it may, at this time, if there is no embedded manifest in the dll AND the dll and manifest have the same name, reuse the same manifest file to setup the dll's activation context.
IF you want to put the "emptyassm" manifest, and dll, in a different folder to your application folder, AND IF you are targetting Windows Server 2008, or Windows 7, or later, you can add a config file for your app :-
c:\apppath\testapp.exe.config
- app config fileThe app config file can contain a probing node under the
assemblyBinding
node (config files look a lot like manifest files), with aprivatePath="some relative path"
. In which case, the relative folder will be searched for assemblies.My last response here has example files covering the process of creating an assembly from a dll, and referencing it from an exe :-
A way to load DLL from central repository
Just to clarify:
A win32 assembly is (At its simplest) a manifest file describing the assembly, and a dll.
They are always, in this model, located in the same folder so the file node of the manifest cannot contain any pathing information at all - only the name of the dll.
Assemblies can be shared - by giving them a strong version (and some digital signing) and installing them in Windows\WinSxS, or private.
Windows versions before 5.1 (Win XP) won't search for assemblies at all as this technology was only added in XP.
Windows 5.1 thru 6.0 (XP and Vista) will only search for private assemblies in the folder of the object with the active activation context :- If an exe references an assembly, then the folder containing the exe. If code in a dll references an assembly then the dll's folder is searched.
If you want to store your dll in a private location shared by several apps (For example) you MUST have a requirement of Windows 7 or later :-
Windows version 6.1 (otherwise known as Windows Server 2008, or Windows 7) and later will, additionally to the module folder, search the path specified as the privatePath element of the probing element in an application configuration file.
Application configuration files are always in the same folder as the exe or dll, and are named:
<exename>.exe.config
, or<dllname>.dll.2.config
(The reason for the .2. is there are potentially lots of manifests and configs embedded as resources, and the loader reserves resources id's 1...15. When searching the disk for a manifest of config file, if the resource id of the embedded resource would have been 1, the id is omitted, but any other number means it becomes part of the file name).
您可以通过使用 Detours 来解决此问题,包装
加载库
。您的 LoadLibrary 包装器将能够识别加载 DLL 的尝试并适当地重写路径。
You may be able to work around this by using Detours, wrapping
LoadLibrary
.Your LoadLibrary wrapper would be able to recognize attempts to load your DLL and rewrite the path appropriately.