您的位置:首页 > 其它

如何解决R6034错误,实现在WIN7以上版本通过LoadLibrary加载msvcr90.dll等DLL

2016-01-14 15:30 435 查看
在XP以前,安装VC运行时库时,安装包只会将各种DLL释放到system32目录并注册相关信息到注册表,这样LoadLibrary时加载这些DLL也不会出错,因为对于应用程序来说,当前只有一个对应的运行时库被注册到系统中,不存在多个不同版本的问题,但也很容易造成兼容性问题,如使用VS2008编译的程序,在仅安装了VS2005运行时库的系统中可能会出现崩溃错误。

到了WIN7,WIN8,WIN10 后,为了在系统中同时提供多种CPU平台和不同VS版本的运行时库来兼容不同的应用软件,微软做了大概下面3个改进,

1)在VS开发时,可在工具中设置manifest来指定当前程序使用的运行时库版本(如win32 x86 版本号 等等),manifest可以嵌入到资源中,也可以放在本地的xml格式的文本文件

2)在C:\Windows\WinSxS中存放各种不同版本的运行时库

有了上面的2个改进,剩下的无非就是系统运行EXE文件或者加载DLL时,如何通过manifest的配置项来正确加载相应的运行时库了。

目前最标准的也是VS开发工具的做法,就是将运行时库通过导入表静态链接到PE文件(EXE或DLL),同时将manifest嵌入到PE文件的资源里,资源号是RT_MANIFEST(24), 再交给系统的PE加载器去自动加载对应的运行时库,虽然本文是说MSVCR90.DLL,但原理上是可以适用于各种各样的运行时库的。

系统在创建进程时,在NTDLL和内核中有部分API和代码统称为PE加载器,在EXE使用LoadLibrary加载DLL时,内部也是一个类似的且专门映射DLL到进程的PE加载器,所以这里说加载器仅仅是一个模块概念,并不是一个实际的EXE程序。

如果采用Loadlibrary去动态加载MSVCR90.DLL,在XP上大部分是正常的,在VISTA/WIN7以上可能就会报错R6034,这是因为调用LoadLibrary的EXE程序没有嵌入包含运行时库依赖信息的manifest,所以加载器无法通过当前进程的manifest找到正确的MSVCR90.DLL,最终报错。

要解决该问题,就需要让加载器能够获取到正确的manifest信息,而manifest信息其实只是一个载体,实际上的使用对象是叫做ActiveContext,也就是说manifest最终会被ActiveContext加载并接管处理。而加载器也是通过ActiveContext来处理被解析映射过的manifest信息。

总体上,就是只要进程中存在正确的ActiveContext,加载器就能够取得正确的依赖库路径并加载。

文章到这里已经相对清晰了,要解决本文提到的问题,现在变成了如何创建包含manifest的ActiveContext,到这里还没有解释ActiveContext是什么,有兴趣的朋友可以查找MSDN中关于“Activation Context”的信息。

下面给出代码展示如何使用LoadLibrary加载msvcr90.dll,先看一段正常但会报错的代码:

procedure LoadMSVCRT();
var
sDllName: string;
begin
sDllName := 'MSVCR90.DLL';
hLib := LoadLibrary(PChar(sDllName));
end;


  上面这段代码在WIN7或WIN10上一执行,通常情况下就会弹出R6034的错误,而以下代码则可避免该问题

procedure LoadMSVCRT();
var
sDllName: string;
hLib: HModule;
aActCtx: TActCtx;
hCtx: THandle;
lpCookie: ULONG_PTR;
begin
// 激活清单文件,可正确加载MSVCR90.DLL
FillChar(aActCtx, SizeOf(TActCtx), 0);
aActCtx.cbSize := SizeOf(TActCtx);
aActCtx.lpSource := PChar(ExtractFilePath(ParamStr(0))+'MSVCRT.manifest');
hCtx := Winapi.Windows.CreateActCtx(aActCtx);
if hCtx<>INVALID_HANDLE_VALUE then
begin
Winapi.Windows.ActivateActCtx(hCtx, lpCookie);
end;
sDllName := 'MSVCR90.DLL';
hLib := LoadLibrary(PChar(sDllName));
if hCtx<>INVALID_HANDLE_VALUE then
begin
DeactivateActCtx(0, lpCookie);
ReleaseActCtx(hCtx);
end;
end;


  以上代码所使用的manifest内容随便找一下就有,具体如下(从某个VC程序的资源中拷贝出来的):

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>


注意其中的版本号,如果系统中没有安装对应的运行时库,仍然会有可能报错。

本文提到的方法同样适用于shellcode对PE文件加壳时需要面对的manifest处理问题。

注:

A . 关于WinSxs(即Windows Side-by-Side),更高级的操作可查找Sxs.dll相关API,号称完美解决不同版本COM和DLL兼容性问题(最初应该是解决.NET不同framework版本的兼容性问题)

B. manifest是一个范围较大的配置文件,可在以后为某些新的功能增加更多配置项,而运行时库依赖配置仅仅是其中一项
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: