C++ 使用清单的 DLL 重定向
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2100973/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
DLL redirection using manifests
提问by WearyMonkey
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.
我需要可靠地重定向查找特定 DLL 的应用程序。使用 app.exe.local 方法不起作用,因为如果应用程序具有清单(嵌入或单独的文件),则会忽略本地文件。所以我试图通过将 DLL 定义为清单中的私有程序集来进行 DLL 重定向。
I have a test application, LoadDll.exe which simply calls
我有一个测试应用程序 LoadDll.exe,它只是调用
LoadLibrary("C:\EmptyDll.dll");
The LoadDll.exe has the manifest (as a separate file, LoadDll.exe.manifest)
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>
The Application folder containing LoadDll.exe (NOT c:\) contains the EmptyDll.dll with the embedded manifest.
包含 LoadDll.exe(不是 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>
However, LoadDll.exe goes ahead and loads C:\EmptyDll.dll, and not the EmptyDll.dll in the application folder.
但是,LoadDll.exe 会继续加载 C:\EmptyDll.dll,而不是应用程序文件夹中的 EmptyDll.dll。
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.
如果您破坏任一清单(例如更改 EmptyDll.dll 清单标识中的版本号),LoadDll.exe 不会加载,因此清单文件正在被 Windows 读取和处理,但只是被忽略。
Anyone got any ideas?
有人有任何想法吗?
Thanks!
谢谢!
Toby
托比
回答by WearyMonkey
So it seems its impossible to redirect calls to LoadLibrary with absolute paths using manifests.
因此,似乎不可能使用清单将调用重定向到具有绝对路径的 LoadLibrary。
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:
基本上,当加载可执行文件时,windows 会收集所有使用标识和依赖项元素链接的相关清单。然后对于清单文件中包含的每个文件元素,它会在激活上下文中添加一个条目:
'name attribute of file element' -> 'absolute path of manifest file' + 'name attribute of file element'
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 does the loadlibrary with the value for that key.
现在,当进行加载库调用时,它会在激活上下文映射中搜索与加载库的路径参数匹配的键,然后使用该键的值执行加载库。
So if my application c:\foo\foo.exe has a dependency to the manifest in c:\foo\baa\baa.manifest, and baa.manifest contains a file element <file name="empty.dll"/>
, then the activation context will have a mapping: "empty.dll" -> "c:\foo\baa\empty.dll"
因此,如果我的应用程序 c:\foo\foo.exe 依赖于 c:\foo\baa\baa.manifest 中的清单,并且 baa.manifest 包含文件 element <file name="empty.dll"/>
,则激活上下文将具有映射:"empty.dll" -> "c:\foo\baa\empty.dll"
So any calls to LoadLibrary("empty.dll")
will be redirected to LoadLibrary("C:\foo\baa\empty.dll")
.
所以任何调用都LoadLibrary("empty.dll")
将被重定向到LoadLibrary("C:\foo\baa\empty.dll")
.
However, LoadLibrary("c:\anotherpath\empty.dll")
Will not be redirected!
但是,LoadLibrary("c:\anotherpath\empty.dll")
不会被重定向!
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 a LoadLibrary("C:\anotherpath\empty.dll")
call, the LoadLibrary call will be redirected to LoadLibrary("C:\foo\baa\C:\anotherpath\empty.dll")
, yes, a malformed path...
现在来证明我的观点,即清单文件和激活上下文是多么愚蠢。如果 baa.manifest 的文件元素是<file name="c:\anotherpath\empty.dll"/>
并且您进行了LoadLibrary("C:\anotherpath\empty.dll")
调用,则 LoadLibrary 调用将被重定向到LoadLibrary("C:\foo\baa\C:\anotherpath\empty.dll")
,是的,格式错误的路径...
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.
file 元素确实有一个未记录的属性,称为“loadFrom”,它听起来像,并且似乎完美地解决了这个问题。使用 loadFrom,我能够重定向绝对路径 loadlibrary 调用,但它似乎以奇怪的方式搞砸了可执行文件中的其他依赖项。如果有人更了解“loadFrom”的工作原理,我会非常感兴趣。
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.
那我最后是怎么解决我的问题的呢?通过使用Ethical Hacker 中描述的令人难以置信的 DLL 特洛伊木马程序。基本上,您创建了一个虚拟的 kernel32.dll,它将所有调用重定向到原始的 kenerl32.dll,除了 LoadLibrary 调用,您可以在其中放置自己的重定向逻辑。然后在应用程序清单中,放置一个文件元素,将 kernel32.dll 重定向到您的虚拟对象。乐趣。
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.
所有这些都描述了我在 Windows Xp Sp2 上的实验。为了额外的乐趣,我相信清单在几乎每个版本的 Windows 上都有不同的表现。
回答by Chris Becke
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 dll
c:\apppath\testapp.exe
- 您的测试应用程序 exe 文件c:\apppath\testapp.exe.manifest
- 潜在的嵌入式应用程序清单文件c:\apppath\EmptyAssm\EmptyAssm.manifest
- 描述新程序集的清单。c:\apppath\EmptyAssm\empty.dll
- 程序集dllc:\apppath\EmptyAssm\empty.dll.2.manifest
- dll 中的嵌入式清单
So, 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.
因此,您的测试应用程序包含一个应用程序清单:其中包含应用程序的依赖程序集引用 - 包括您添加到自定义 dll 程序集的引用。
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".
在app 文件夹的application 文件夹assm 子文件夹中,您有“EmptyAssm”程序集的程序集清单,其中包含引用实际 dll“empty.dll”的文件节点。
empty.dll embeds its own manifest, containing dependent assembly references on any public or private assemblies it requires.
empty.dll 嵌入了它自己的清单,包含对它需要的任何公共或私有程序集的依赖程序集引用。
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.
这是重点:“EmptyAssm”程序集清单和“空”dll 清单可能不同。("EmptyAssm") 程序集的清单文件不得嵌入,但如果您选择以 dll 的名称命名清单,则可能会共享 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.
现在,当加载程序加载您的 EXE 时,它会加载您的 EXE 的清单并将其添加到激活上下文中。当处理 EXE 的导入表时,或者您调用 LoadLibrary,加载程序首先在激活上下文中搜索具有匹配文件节点的程序集清单。如果找到匹配的程序集,则它会从程序集位置(包含程序集 .manifest 的文件夹)处理并加载 dll,此时,如果 dll 中没有嵌入的清单,并且 dll 和清单有相同的名称,重用相同的清单文件来设置 dll 的激活上下文。
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 :-
如果您想将“emptyassm”清单和 dll 放在与您的应用程序文件夹不同的文件夹中,并且如果您的目标是 Windows Server 2008、Windows 7 或更高版本,您可以为您的应用程序添加一个配置文件:-
c:\apppath\testapp.exe.config
- app config file
c:\apppath\testapp.exe.config
- 应用程序配置文件
The app config file can contain a probing node under the assemblyBinding
node (config files look a lot like manifest files), with a privatePath="some relative path"
. In which case, the relative folder will be searched for assemblies.
应用程序配置文件可以在节点下包含一个探测节点assemblyBinding
(配置文件看起来很像清单文件),扩展名为privatePath="some relative path"
. 在这种情况下,将在相关文件夹中搜索程序集。
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
我在这里的最后一个回复有示例文件,涵盖了从 dll 创建程序集的过程,以及从 exe 引用它的过程:- 从中央存储库加载 DLL 的方法
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.
澄清一下:win32 程序集是(最简单的)一个描述程序集的清单文件和一个 dll。在这个模型中,它们总是位于同一个文件夹中,因此清单的文件节点根本不能包含任何路径信息——只有 dll 的名称。
Assemblies can be shared - by giving them a strong version (and some digital signing) and installing them in Windows\WinSxS, or private.
程序集可以共享——通过给它们一个强大的版本(和一些数字签名)并将它们安装在 Windows\WinSxS 或私有中。
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.
5.1 (Win XP) 之前的 Windows 版本根本不会搜索程序集,因为此技术仅在 XP 中添加。Windows 5.1 到 6.0(XP 和 Vista)将仅在具有活动激活上下文的对象文件夹中搜索私有程序集:- 如果 exe 引用程序集,则包含该 exe 的文件夹。如果 dll 中的代码引用程序集,则搜索 dll 的文件夹。
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 :-
如果您想将您的 dll 存储在由多个应用程序共享的私人位置(例如),您必须有 Windows 7 或更高版本的要求:-
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:
除了模块文件夹之外,Windows 6.1 版(也称为 Windows Server 2008 或 Windows 7)及更高版本将搜索指定为应用程序配置文件中探测元素的 privatePath 元素的路径。应用程序配置文件始终与 exe 或 dll 位于同一文件夹中,并命名为:
<exename>.exe.config
, or <dllname>.dll.2.config
<exename>.exe.config
, 或者 <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).
(.2. 的原因是可能有很多清单和配置作为资源嵌入,并且加载程序保留资源 ID 的 1...15。在磁盘搜索配置文件的清单时,如果嵌入资源应该是 1,id 被省略,但任何其他数字意味着它成为文件名的一部分)。