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

发布时间:2019-01-28编辑:佚名阅读(2156)

自从操作系统升级到64位以后,就要不断的需要面对32位、64位的问题。相信有很多人并不是很清楚32位程序与64位程序的区别,以及Program Files (x86),Program Files的区别。同时,对于程序的dll文件应该放到System32文件夹,还是SysWow64,大部分人做的决定是,32位程序放到System32,64位程序放到SysWow64。是不是这样呢?

dll文件不匹配导致软件无法启动

当EXE软件和引用的dll位数不匹配时会报此错误。

究竟是System32还是SysWow64

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

SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。

当32位程序加载System32文件夹中的dll时,操作系统会自动映射到SysWow64文件夹中的对应的文件。

区分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位了,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位程序,根据常识也可以判断出,实际肯定不是这样的。一定是程序出了什么问题,那么直接用十六进制编辑器看一下两个文件是否一致吧。

再次判断究竟是System32还是SysWow64

通过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文件夹”是正确的。

Program Files (x86)与Program Files

由System32与SysWow64的情况,考虑到Program Files (x86)与Program Files是不是也是这种情况。当32位程序访问Program Files目录时,会被自动映射到Program Files (x86)目录?

还是通过1.txt的方式来验证,发现当32位程序访问Program Files目录时,并没有被映射到Program Files (x86)目录。

32位程序真的需要访问System32吗

经过了这么多验证,总算是知道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文件。

32位程序与64位程序的区别总结

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


评论区