首页 > 学院 > 开发设计 > 正文

dll文件32位64位检测工具以及Windows文件夹SysWow64的坑

2019-11-17 03:18:51
字体:
来源:转载
供稿:网友

dll文件32位64位检测工具以及Windows文件夹SysWow64的坑

自从操作系统升级到64位以后,就要不断的需要面对32位、64位的问题。相信有很多人并不是很清楚32位程序与64位程序的区别,以及PRogram Files (x86),Program Files的区别。同时,对于程序的dll文件应该放到System32文件夹,还是SysWow64,大部分人做的决定是,32位程序放到System32,64位程序放到SysWow64。是不是这样呢,那么今天就由我身边发生的一个案例来详细的说明一下。

dll文件不匹配导致数据库无法启动

前段时间,数据库做了一些功能上的改进,于是用VS2010编译检出了一个版本,供测试部测试。测试部拿到数据库后,通过批处理将数据库程序,注册为服务。虽然执行的是批处理,实际上注册服务的过程,是通过运行数据库程序,并给其传入命令行参数来完成的,详情请看这篇文章玩转Windows服务系列——Debug、Release版本的注册和卸载,及其原理。

通过批处理运行程序后,出现如下问题:

应用程序无法启动

出现这种问题,测试部不淡定了,叫我去看。我又试着运行了一下程序,依然出现这个问题。“可是在我的机器上运行的挺好的啊”,这是我说的第一句话,相信很多人看了这句话就会心的笑了。

有问题就是有问题,既然我的机器上可以正常运行,那么测试机为什么不行呢,首先要查找原因。

数据库是用VS2010编译的,那么在其他机器上运行,就需要运行的操作系统中以及安装了VS2010的运行时,否则就会因为缺少程序运行所必须的dll文件而无法正常运行。我想应该是这个原因,但又一想,如果没有装运行时的话,会提示缺少msvcr100.dll、msvcp100.dll等文件,上图中的问题显然不是缺少dll问题。问题有点复杂,为了简单,先试着安装运行时,看能不能解决吧。

将VS2010的x86和x64 Runtime安装包全装了一遍。再运行程序,依然是这个醒目的错误。

虽然安装运行时没有解决这个问题,但根据经验判断,要么是缺少dll文件,要么就是dll文件版本出了问题。那么,接下来就是想办法证明这个猜想。

通过Dependency Walker检测数据库程序,所有依赖的dll文件都存在,没有发现什么问题。然后通过Windows Sysinternals中的ListDLLs工具检测当前运行的进程已经加载的dll文件,从列表中看到msvcr100.dll没有加载,估计就是这个dll文件出了问题。从我的机器上找到这个文件,替换了测试机上的msvcr100.dll文件后,数据库就正常运行了。

原来,刚刚启动数据库的时候,提示找不到msvcr100.dll文件,测试的同事就从其他的XP系统的机器上找了这个文件,并分别放入到System32和SysWow64中,于是就导致了上图中的这个问题。

由于XP系统是32位的,所以找到的msvcr100.dll文件也是32位,当把这个32位程序放到System32文件夹后,启动64位数据库,就会加载这个32位dll,由于64位程序只能加载64位dll,所以当程序尝试加载32位dll时,就会报错了。

究竟是System32还是SysWow64

Win7、Server2008等64位系统出来以后,为了兼容32位程序,所以采用了Wow64方案,在系统文件夹中,可以看到一个System32文件夹,和一个SysWow64文件夹。虽然这个方案对于程序来说,可以很方便的兼容32位程序,但是对于一般用户来说,想分辨System32和SysWow64那就有点困难了,因为名字太有迷惑性了。

至于微软为什么采用Wow64方案,我就不细说了,感兴趣的朋友可以看这篇文章:什么是SysWow64。这篇文章详细的介绍了Wow64技术,以及64位系统兼容32位程序的情况。

最后,我们可以知道:

  • SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
  • 当32位程序加载System32文件夹中的dll时,操作系统会自动映射到SysWow64文件夹中的对应的文件。

看到这些,你一定会认为你真正的明白了System32和SysWow64的区别,我也一样,我以为我真的懂了,但是真的懂了吗,是真懂了吗?

无论怎样还是请你坚持看完。

区分dll文件32位64位的程序让我倍感迷惑

上面说到的数据库无法启动的这种情况,已经遇到了不止一次了。每次遇到这种问题,我都想能不能有个工具可以检查System32和SysWow64文件夹中的dll程序是不是对应的64位和32位程序。据我所知只有dumpbin可以查看一个dll文件是32位还是64位,但它明显不是我想要的工具,因为每次只能查看一个文件。

好吧,自己动手,丰衣足食,既然没有这种工具,那就来写一个吧,好在判断dll文件是32位还是64位也不是很难。

Windows系统下,exe、dll文件都可以称为PE文件,他们有相同的文件格式,称为PE文件格式。

PE文件的第一个部分是IMAGE_DOS_HEADER,大小为64B,对于检查32位64位来说,有一个重要的成员e_lfanew,这个成员的值为IMAGE_NT_HEADERS的偏移。

IMAGE_DOS_HEADER的定义如下:

typedef struct _IMAGE_DOS_HEADER{//(注:最左边是文件头的偏移量。)+0h  Word e_magic         //Magic DOS signature MZ(4Dh 5Ah)         DOS可执行文件标记+2h  WORD e_cblp          //Bytes on last page of file  +4h  WORD e_cp            //Pages in file+6h  WORD e_crlc          //Relocations+8h  WORD e_cparhdr       //Size of header in paragraphs+0ah WORD e_minalloc      //Minimun extra paragraphs needs+0ch WORD e_maxalloc      //Maximun extra paragraphs needs+0eh WORD e_ss            //intial(relative)SS value                DOS代码的初始化堆栈SS+10h WORD e_sp            //intial SP value                         DOS代码的初始化堆栈指针SP+12h WORD e_csum          //Checksum+14h WORD e_ip            //intial IP value                         DOS代码的初始化指令入口[指针IP]+16h WORD e_cs            //intial(relative)CS value                DOS代码的初始堆栈入口+18h WORD e_lfarlc        //File Address of relocation table+1ah WORD e_ovno          //Overlay number+1ch WORD e_res[4]        //Reserved words+24h WORD e_oemid         //OEM identifier(for e_oeminfo)+26h WORD e_oeminfo       //OEM information;e_oemid specific +29h WORD e_res2[10]      //Reserved words+3ch DWORD e_lfanew       //Offset to start of PE header            指向PE文件头} IMAGE_DOS_HEADER;

IMAGE_NT_HEADERS的定义如下:

typedef struct _IMAGE_NT_HEADERS { +0h  DWORD                     Signature;+4h  IMAGE_FILE_HEADER         FileHeader;+18h IMAGE_OPTIONAL_HEADER32   OptionalHeader;} IMAGE_NT_HEADERS;

Signature 字段:在一个有效的 PE 文件里,Signature 字段被设置为00004550h,ASCII 码字符是“PE00”。标志这 PE 文件头的开始。“PE00” 字符串是 PE 文件头的开始,DOS 头部的 e_lfanew 字段正是指向这里。

IMAGE_FILE_HEADER 结构定义:

typedef struct _IMAGE_FILE_HEADER {+04h  WORD  Machine;                        // 运行平台+06h  WORD  NumberOfSections;               // 文件的区块数目+08h  DWORD TimeDateStamp;                  // 文件创建日期和时间+0Ch  DWORD PointerToSymbolTable;           // 指向符号表(主要用于调试)+10h  DWORD NumberOfSymbols;                // 符号表中符号个数(同上)+14h  WORD  SizeOfOptionalHeader;           // IMAGE_OPTIONAL_HEADER32 结构大小+16h  WORD  Characteristics;                // 文件属性} IMAGE_FILE_HEADER;

其中Machine字段表示可执行文件的目标CPU类型:

  • IMAGE_FILE_MACHINE_I386         0x014c   x86
  • IMAGE_FILE_MACHINE_IA64         0x0200   Intel Itanium
  • IMAGE_FILE_MACHINE_AMD64        0x8664  x64

这样不是很直观,上张图来看一下:

32位64位PE文件

有了这些,我们就可以通过程序来判断32位、64位了,代码如下:

public static bool IsPE32(string path){    FileStream file = File.OpenRead(path);    //移动到e_lfanew的位置处    stream.Seek(0x40 - 4, SeekOrigin.Begin);    byte[] buf = new byte[4];    stream.Read(buf, 0, buf.Length);    //根据e_lfanew的值计算出Machine的位置    int pos = BitConverter.ToInt32(buf,0) + 4;    stream.Seek(pos, SeekOrigin.Begin);    buf = new byte[2];    stream.Read(buf, 0, buf.Length);    //得到Machine的值,0x14C为32位,0x8664为64位    Int16 machine = BitConverter.ToInt16(buf, 0);    if (machine == 0x14C)    {        return true;    }    else    {        return false;    }}

最核心的功能完成了,剩下的就是界面和遍历文件夹了,效果图:

检测效果图

根据检测结果和实际情况判断,检测结果没问题。那么就开始真正的检测吧,System32和SysWow64。检测结果如下图:

32位程序System32SysWow64检测结果对比

从图中看出,System32、SysWow64中检测出的所有的文件均为32位程序,根据常识也可以判断出,实际肯定不是这样的。一定是程序出了什么问题,那么直接用十六进制编辑器看一下两个文件是否一致吧。

再次判断究竟是System32还是SysWow64——意想不到的坑

通过UE查看两个文件夹中的msvcr110d.dll确实都是32位程序,而且用Beyond Compare进行比较,两个文件也没有差异。用工具查看两个文件的md5也是完全一致:

System32_SysWow64_msvcr110d_md5

难道两个文件真的都是32位吗,我还是觉得不太可能。

接下来将System32和SysWow64中的msvcr110d.dll分别移动到其他文件夹,这样System32和SysWow64就没有这个dll文件了,然后运行一个32位的需要这个dll文件的程序PeTest,提示找不到这个dll文件。分别将原来System32和SysWow64中的msvcr110.dll拷贝到这个PeTest所在的目录,运行程序。当使用SysWow64中的msvcr110d.dll时,程序可以正常运行,说明这个文件确实是32位。当使用System32中的msvcr110d.dll时,程序无法正常运行,出现文章开始时提到的错误。为什么通过Beyond C

上一篇:CLR via C#

下一篇:装箱算法实验

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表