DLL redirection using manifests
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
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:
'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 calls Loadlibrary
with the value for that key.
So if my application c:\foo\foo.exe
has a dependency on 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 the mapping: "empty.dll" -> "c:\foo\baa\empty.dll"
So any calls to LoadLibrary("empty.dll")
will be redirected to LoadLibrary("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 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...
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.
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
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.
In the application folder assm 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 file
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.
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).
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.
精彩评论