终结VC2005分发包版本问题

之前曾经写过一篇个人经历,是关于VC2005分发包版本不一致而引起应用程序无法正常启动的(http://www.cnblogs.com/mixiyou/archive/2009/10/02/1575311.html)。这篇文章里只是介绍了个人在开发过程中遇到的一个非常极品的问题,但是没有给出更多详细的信息和解决方法。今日偶然看到一篇牛人写的文章,顺手翻译下来,总算了了长久以来的心愿。

原文地址:http://dumian.spaces.live.com/blog/cns!B67A33A47F9F9C48!459.entry

 

/////////////////////////////////////////////// 原文 ////////////////////////////////////////////

VC redistributable security update -KB971090 mitigation problem

Problem

After installing KB971090 for Visual C++ 2005 SP1, the version of ATL/MFC/CRT referenced in the generated manifests is changed from 8.0.50727.762 to 8.0.50727.4053

The above change forces the application developer to rebuild all components to reference the 4053 version of the versions of ATL/MFC/CRT in the manifest, as well as making it necessary to ship a new vcredist_x86.exe and/or MSM files containing the new 4053 versions of ATL/MFC/CRT.

Solution

Instead of uninstalling the KB971090 update, we can force the build to use the 8.0.50727.762 version instead of the default which is 8.0.50727.4053. Microsoft provides a mechanism to specify the assembly version number to use in the manifests.

Steps to take to allow your applications to reference 8.0.50727.762 instead of 8.0.50727.4053

1) Create a file named targetsxs.h with the following content and put it in a common location

#ifndef __midl
#define _SXS_ASSEMBLY_VERSION "8.0.50727.762"
#define _CRT_ASSEMBLY_VERSION _SXS_ASSEMBLY_VERSION
#define _MFC_ASSEMBLY_VERSION _SXS_ASSEMBLY_VERSION
#define _ATL_ASSEMBLY_VERSION _SXS_ASSEMBLY_VERSION

#ifdef __cplusplus
extern "C" {
#endif
__declspec(selectany) int _forceCRTManifest;
__declspec(selectany) int _forceMFCManifest;
__declspec(selectany) int _forceAtlDllManifest;
__declspec(selectany) int _forceCRTManifestRTM;
__declspec(selectany) int _forceMFCManifestRTM;
__declspec(selectany) int _forceAtlDllManifestRTM;
#ifdef __cplusplus
}
#endif
#endif

2) The above file needs to be included in every project you wish to build.

Accomplish this in one of two ways. The first method involves the changing of source code. The second involves changing of project settings.

choose A or B which ever one is more appropriate for your situation:

A) #include "targetsxs.h" near the top of every one of your stdafx.h files
(using the path you saved the targetsxs.h)

B) Use the force include (/FI) compile option to make sure that the above file is included in every file built. To accomplish that, add the following command line to your compiler options

/FIc:\path\targetsxs.h

(where c:\path is the location of the targetsxs.h you created in step 1)

There are two methods to accomplish that: either directly in your project or through a compiler environment variable:

Method i)

Add the above to the command line section of the C/C++ project properties.

Method ii)

Create an environment variable named CL with the above-mentioned /FI statement

Method (ii) is more convenient to use when you have dozens of projects that you do not wish to make changes to, but may conflict with other work you do on your machine.

3) For every MFC DLL project in your solution add the following line to the bottom of the stdafx.cpp file

#include "..\src\mfc\dllmodul.cpp"

Step three is only necessary if you have any MFC DLL projects (this step is necessary due to a bug in the handling of the assembly versions in MFC DLLs – the bug is that a reference to the latest version of the DLLs will always be included regardless of what is set in step 1)

4) Do a rebuild all of all the projects in your solution

5) Inspect manifest files (by doing a find in files for *.intermediate.manifest) to ensure that 4053 no longer appears in any of the manifests.

6) Run your app on a machine that has no 4053 components installed to WinSxS to make sure runs properly.

IMPORTANT NOTE: if you have ATL controls that are affected by this security problem please ensure you have followed the directions to modify your controls to take advantage of the security updates. A simple recompile may not be enough. For more info please see the video at:

http://channel9.msdn.com/posts/Charles/Out-of-Band-Inside-the-ATL-Security-Update/

Q&A

Does referencing 762 instead of 4053 make your application less secure?

No. Microsoft provides WinSxS policy redirection, so any application referencing 762 will end up loading the 4053 versions (if they’re installed). The ATL security update redistributable has been released as an update on Windows Update, so the 4053 version of ATL will be in place, and will be used even though your app still references the 762 version of ATL.

*Important note: you should still ship the new version of the redistributable if at all possible, as users may have not updated using Windows Update.

What are the benefits to referencing 762 instead of 4053 in your SP1 applications?

1) If you have existing applications shipped and you need to rebuild any components shipped, then new MSM or vcredist_x86.exe containing 4053 versions do not need to be shipped. You can continue to ship existing components with 762 versions.

2) If you have existing third party static libraries that reference 762, then you need not rebuild them to reference 4053.
Does VC2008 SP1 security update have a similar issue?
Yes, it does, if you use _BIND_TO_CURRENT_VCLIBS_VERSION in your project. It will bind to the security update version instead of the SP1 version. You can use a similar technique as described above (step 3 is not required) to get around this problem.

For VC2008 SP1 the values for the current versions of the CRT/MFC/ATL have changed slightly, instead of _forceCRTManifest, you should use _forceCRTManifestCUR.  The same thing applies for MFC/ATL (add the CUR). And of course, the version numbers you’re dealing with are different: SP1 9.0.30729.1 vs the new SP1+security update which is 9.0.30729.4148

 

///////////////////////////////////////// 译文 /////////////////////////////////////////

VC分发包安全更新--KB971090的修正之道

 

[注]

KB971090的描述:http://support.microsoft.com/kb/971090/zh-cn

KB971090的下载地址:http://www.microsoft.com/downloads/details.aspx?familyid=7C8729DC-06A2-4538-A90D-FF9464DC0197&displaylang=zh-cn

 

[问题描述]

在安装了Visual C++ 2005 SP1的KB971090安全更新后,VC生成的manifest中引用的ATL/MFC/CRT库的版本从8.0.50727.762变成了8.0.50727.4053。

正因为上面的变动,导致了应用程序开发者必须重新编译所有的组件使它们在manifest中指向4053版本的ATL/MFC/CRT库,同时在发布应用程序的时候还必须部署一个新的vcredist_x86.exe(对应8.0.50727.4053版本)或者在安装文件中内嵌包含了最新4053版本的ATL/MFC/CRG库的MSM文件。

 

[解决之道]

除了卸载KB971090更新之外,我们还可以强制使用8.0.50727.762版本的库来替换此时默认8.0.50727.4053的库。Microsoft提供了一种在manifest文件中指定使用哪种版本编译库的机制。

使得你的应用程序从指向8.0.50727.4053变为指向8.0.50727.762的步骤如下:

1) 创建一个名为targetsxs.h的头文件,填入下面的内容,保存在某个固定目录下。

#ifndef __midl
#define _SXS_ASSEMBLY_VERSION "8.0.50727.762"
#define _CRT_ASSEMBLY_VERSION _SXS_ASSEMBLY_VERSION
#define _MFC_ASSEMBLY_VERSION _SXS_ASSEMBLY_VERSION
#define _ATL_ASSEMBLY_VERSION _SXS_ASSEMBLY_VERSION

#ifdef __cplusplus
extern "C" {
#endif
    __declspec(selectany) int _forceCRTManifest;
    __declspec(selectany) int _forceMFCManifest;
    __declspec(selectany) int _forceAtlDllManifest;
    __declspec(selectany) int _forceCRTManifestRTM;
    __declspec(selectany) int _forceMFCManifestRTM;
    __declspec(selectany) int _forceAtlDllManifestRTM;
#ifdef __cplusplus
}
#endif
#endif

 

2) 你需要在每个你希望编译的项目中包含上面的头文件。

可以通过两种方法来达成这个目的。第一个方法是涉及到修改源代码。第二个方法涉及到修改项目设置。到底选择方法A好还是方法B好?这适合取决于你当前的应用环境:

A) 在你的每一个stdafx.h头文件的头部包含"targetsxs.h"头文件

(使用你保存targetsxs.h的路径)

B) 强制使用(/FI)编译选项来保证每个文件编译时都包含上面的头文件(targetsxs.h)。为了达到这个目的,你可以在你的编译器选项中添加如下的命令行

/FIc:\path\targetsxs.h

(c:\path是你在第一步中创建的targetsxs.h的保存位置)

可以使用两个方法在编译器选项中到达这个目的:在你的工程中直接设置或者通过编译器环境变量来设置:

方法1:

将上面的命令添加到C/C++工程属性的命令行中

方法2:

用上面提到的/FI语法创建一个名为CL的环境变量

当你有很多工程的时候,你肯定不希望一个一个地去修改它们的工程属性,这个时候方法2就方便很多,但同时这个环境变量也可能会影响到你机器上的其他工程。

 

3) 对于你项目清单中的每个MFC DLL工程,你需要在stdafx.cpp文件的底部添加下面一行代码

#include "..\src\mfc\dllmodul.cpp"

只有当你使用了MFC DLL工程的时候,这一步才是必需的(之所以是必需的是因为在MFC DLL中处理汇编代码(assembly versions)时的一个Bug - 如果没有包含dllmodul.cpp的话,步骤一设置的东西就不会被包含而编译器会始终引用最新版本的DLL)。

 

4) 编译你的项目清单中的所有工程。

 

5) 检查manifeset文件(就是编译器生成的那些*.intermediate.manifest文件)以确保4053不会出现在任何manifest文件中。

 

6) 在一台没有安装4053版本WinSxS组件机器上运行你的应用程序来验证它可以正常运行。

 

重要提示:如果你被ATL控件的安全性问题所影响(而必须要安装这个更新)的话,还是请你安装此更新以便使用更加安全的ATL控件。此时,简单的重编译可能并不足够,比需要从下面的视频中得到更多的信息:

http://channel9.msdn.com/posts/Charles/Out-of-Band-Inside-the-ATL-Security-Update/

 

 

 

Q&A

 

1.使用762版本的组件来代替4053是否会使得你的应用程序安全性降低?

不会。微软提供了一种WinSxS重定位策略,所以任何引用到762版本ATL的应用程序都最终会应用到4053版本(如果你安装了它们的话)。这个ATL安全更新已经作为Window Update的更新发布了,所以一旦你保持了与Window Update同步的更新,那么你就已经准备好使用4053版本的ATL了,即使你的应用程序仍然引用了762版本的ATL,你最终还是会使用4053版本的ATL。

*重要提示:如果可能的话,你最好还是把最新版本的分发包随你的应用程序一起发布,因为用户有可能并不会使用Window Update来更新最新的组件。

 

2.在你的SP1应用程序中引用762版本来代替4053有什么好处么?

1) 如果你有一些应用程序已经发布了但是必须重新编译某些已经发布出去的组件,那么你就不用再发布新的MSM或者包含4053版本的vcrdist_x86.exe了。你可以继续使用762版本来发布现有的组件。

2) 如果你有使用引用到762版本的第三方静态库的话,你也不需要将它们重新编译到4053版本。

 

3.VC2008 SP1安全更新是否有同样的问题呢?

是的,如果你在你的工程中使用了_BING_TO_CURRENT_VCLIBS_VERSION,那么就会有同样的问题。它将会绑定到最新版本的安全更新来代替原来的SP1版本。你同样可以使用上面描述的方法(步骤3不需要)来解决这个问题。
对于VC2008 SP1,CRT/MFC/ATL的当前版本号有些许变化,你应该使用_forceCRTManifestCUR来代替_forceCRTManifest。对于MFC/ATL也必须使用同样的设置(添加CUR)。当然,此时你所面对的版本号也是不同的:SP1是9.0.30729.1,而最新的SP1+安全更新则是9.0.30729.4148。

 

 

 

PS:由于个人实在无法忍受,每次编译出来的DLL都要手动去修改其资源文件中的Manifest(使用ResHacker删除掉有关4053版本的Manifest节点),所以今天一狠心,直接将工作机上所有关于VC2005的东东全部干掉,再重新安装一次,虽然浪费了很多时间,但是还是这种釜底抽薪的方法最有效。 

posted @ 2010-02-09 14:27  芈希有  阅读(3361)  评论(1编辑  收藏  举报