发布时间:2019-01-28编辑:佚名阅读(2156)
自从操作系统升级到64位以后,就要不断的需要面对32位、64位的问题。相信有很多人并不是很清楚32位程序与64位程序的区别,以及Program Files (x86),Program Files的区别。同时,对于程序的dll文件应该放到System32文件夹,还是SysWow64,大部分人做的决定是,32位程序放到System32,64位程序放到SysWow64。是不是这样呢?
当EXE软件和引用的dll位数不匹配时会报此错误。
Win7、Server2008等64位系统出来以后,为了兼容32位程序,所以采用了Wow64方案,在系统文件夹中,可以看到一个System32文件夹,和一个SysWow64文件夹。虽然这个方案对于程序来说,可以很方便的兼容32位程序,但是对于一般用户来说,想分辨System32和SysWow64那就有点困难了,因为名字太有迷惑性了。
SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
当32位程序加载System32文件夹中的dll时,操作系统会自动映射到SysWow64文件夹中的对应的文件。
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位了,C++代码如下:
#include <stdio.h> #include <windows.h> int CrnGetImageFileMachine(char* lpFileName); int main() { int n = CrnGetImageFileMachine("C:\\a.dll");//需要检测的可执行文件 if (n == 0x014C) printf("x86\n");//32位 else if (n == 0x0200) printf("IA64\n");//纯64位 else if (n == 0x8664) printf("x64\n");//64位 else printf("未知\n"); return 1; } int CrnGetImageFileMachine(char* lpFileName) { IMAGE_DOS_HEADER idh; FILE *f = fopen(lpFileName, "rb"); fread(&idh, sizeof(idh), 1, f); IMAGE_FILE_HEADER ifh; fseek(f, idh.e_lfanew + 4, SEEK_SET); fread(&ifh, sizeof(ifh), 1, f); fclose(f); return ifh.Machine; }
C#代码:
public static bool IsPE32(string path) { FileStream file = File.OpenRead(path); //移动到e_lfanew的位置处 file.Seek(0x40 - 4, SeekOrigin.Begin); byte[] buf = new byte[4]; file.Read(buf, 0, buf.Length); //根据e_lfanew的值计算出Machine的位置 int pos = BitConverter.ToInt32(buf,0) + 4; file.Seek(pos, SeekOrigin.Begin); buf = new byte[2]; file.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。检测结果如下图:
从图中看出,System32、SysWow64中检测出的所有的文件均为32位程序,根据常识也可以判断出,实际肯定不是这样的。一定是程序出了什么问题,那么直接用十六进制编辑器看一下两个文件是否一致吧。
通过UE查看两个文件夹中的msvcr110d.dll确实都是32位程序,而且用Beyond Compare进行比较,两个文件也没有差异。用工具查看两个文件的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 Compare、UE、MD5检测为同样的dll文件,一个可以正常运行,另外一个就不可以呢。
再次思考这两句话:
SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
当32位程序加载System32文件夹中的dll时,操作系统会自动映射到SysWow64文件夹中的对应的文件。
32位程序加载System32文件夹中的dll文件,操作系统会自动映射到SysWow64文件夹,也就是说64位程序,系统不会再做映射。
通过任务管理器查看UE、Beyond Compare和MD5三个进程全部为32位进程,即三个程序全部是32位。
至此我们可以重新理解“32位程序加载System32文件夹中的dll文件,操作系统会自动映射到SysWow64文件夹”这句话,应该是“只要32位程序访问System32文件夹,无论是加载dll,还是读取文本信息,都会被映射到SysWow64文件夹”。
这个理解对吗,我们来做一个实验验证一下。
找一个32位的文本编辑器Notepad++(Win7系统自带的是64位),在SysWow64文件夹中新建一个1.txt的文件,打开编辑此文件,内容为SysWow64。然后在打开文件对话框中的输入框中输入“C:\Windows\System32\1.txt”,打开文件,看到内容为SysWow64,如图所示:
System32中没有创建1.txt文件,32位程序访问System32中的1.txt文件,被自动映射到SysWow64文件夹中的1.txt文件,而如果用64位的Notepad编辑器打开System32中的1.txt文件,就会提示找不到文件:
由此就可以验证猜想“只要32位程序访问System32文件夹,无论是加载dll,还是读取文本信息,都会被映射到SysWow64文件夹”是正确的。
由System32与SysWow64的情况,考虑到Program Files (x86)与Program Files是不是也是这种情况。当32位程序访问Program Files目录时,会被自动映射到Program Files (x86)目录?
还是通过1.txt的方式来验证,发现当32位程序访问Program Files目录时,并没有被映射到Program Files (x86)目录。
经过了这么多验证,总算是知道32位程序无法访问System32,只有64位程序才能访问,由此认为,这是Windows的一个非常大的坑。但是仔细想想,32位程序真的需要访问System32吗。就用这个dll检测工具来说吧。
如果在64位系统上,32位程序无法访问System32,为了访问它,就需要编译为64位。而如果程序编译为64位,就无法在32位系统上运行,同时由于在32位系统上不需要检测32位、64位,所以只需要32位程序即可。这可真是一个矛盾的事情,难道必须编译两个程序,一个32位,一个64位,来适应不同的操作系统吗。如果是C++的话,那么答案是这样的,必须编译一个32位一个64位。而DotNet就不一样了,编译的时候选择“AnyCPU”,并且不选择“首选32位”(VS2012中默认选中),编译后的程序,可以同时在32位和64位系统上运行,32位系统上是32位进程,64位系统上是64位进程,是不是很方便呢,这正是DotNet和AnyCPU的魅力所在。
至此,dll检测程序,不需要做任何代码修改,只需在编译的时候选择AnyCPU,并去掉“首选32位”选项,即可正常检测System32、SysWow64文件夹中的dll文件。
SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
.Net程序以AnyCPU配置,并选择“首选32位”编译,会以32位的进程运行,此时就无法访问System32文件夹中的文件;如果没有选择“首选32位”,则会以64位的进程运行,这样就可以访问System32文件夹了。(VS2012中,“首选32位”默认是选中的)。
32位程序的寻址空间有限,最多达到4G,而64位程序的寻址空间可以达到TB级,想要使用大内存的话,就升级到64位吧,好在DotNet程序从32位升级到64位比较简单,不像C++那么麻烦。
32位程序访问System32目录,会自动被映射到SysWOW64目录,而64位程序可以访问System32目录和SysWOW64目录。
32位程序与64位程序有各自的注册表。
32位与64位程序都可以访问Program Files (x86)与Program Files目录。
关键字: dll 32位 64位 SysWow64 0xc000007b
0人
0人
0人
0人