如何判断一个 Dot Net 程序是 32 位还是 64 位?

简介: 如何判断一个 Dot Net 程序是 32 位还是 64 位?

缘起

前阵子,朋友遇到一个 .net 程序启动不起来的问题。根据之前的经验,一般是依赖的动态库加载失败导致的。或者找不到(依赖的动态库没有放到相应的目录下,一般放到应用程序所在目录即可),或者不匹配(64 位的程序加载 32 位的动态库,或者 32 位的程序加载 64 位的动态库)。整个排查过程并不复杂,本文不打算介绍整个排查过程,而是想介绍一些 .net 程序的基本常识(比如,以 Any CPU 编译出来的程序,是 32 位的还是 64 位的?),还会介绍几个我认为不错的查看工具。

在介绍查看方法之前,先介绍一些基本常识。

Any CPU

做过 .net 开发的小伙伴一定接触过 Any CPU ,新建一个 c# 测试工程,默认的编译选项就是这个。

csharp-compile-option.png

目标平台(G)首选 32 位(P) 两个选项共同决定了传递给 csc.exe/platform 选项的值。

目标平台(G)Any CPU的情况下,如果勾选了 首选 32 位(P),那么 /platform 的值是 anycpu32bitpreferred,如果未勾选,那么 /platform 的值是 anycpu

说明: 首选 32 位(P) 选项在 dll 工程中不允许修改。虽然编译的时候不能改,但是我们可以手动修改编译后的文件。:)

/platform 选项对生成的模块的影响以及在运行时的影响,参考下表:

/platform 开关 生成的托管模块 x86 Windows x64 Windows ARM Windows RT
anycpu(默认) PE32 / 任意 CPU 架构 作为 32 位应用程序运行 作为 64 位应用程序运行 作为 32 位应用程序运行
anycpu32bitpreferred PE32 / 任意 CPU 架构 作为 32 位应用程序运行 作为 WoW64 位应用程序运行 作为 32 位应用程序运行
x86 PE32 / X86 作为 32位应用程序运行 作为 WoW64 位应用程序运行 不运行
x64 PE32+ / X64 不运行 作为 64 位应用程序运行 不运行
ARM PE32 / ARM 不运行 不运行 作为 32 位应用程序运行

说明:以上表格摘录自 《CLR via c#》(第4版)第一章

PE 头相关字段

一般,一个标准的 PE 文件由四大部分组成: DOS 头,PE 头,节表,节内容。这里只关心 PE头中相关字段。

32PE 头定义如下:

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

其中,Signature 的内容是 PE\0\0,非常好认。

FileHeader 对应的结构体定义如下:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
  • Machine ,对于 32 位程序,这个值一般是 0x14c,对于 64 位程序一般是 0x8664。但对于 .net 程序,不能以此字段作为判断依据。

OptionalHeader 对应的结构体定义如下:

typedef struct _IMAGE_OPTIONAL_HEADER32 {
    WORD        Magic;
    // ... 省略无关字段
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 一共16项
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
  • Magic 如果为 010B,表示这是一个 PE32 文件,如果为 020B 表示这是一个 PE32+ 文件,也就是 64 位的 PE 文件。

    FileHeader.Machine 一样,对于 .net 程序,不能以此字段作为判断依据。

  • DataDirectory 中一共有 16 项。其中,最后一项是保留项,第 14 项(索引从 0 开始)指向了 CLR 的结构。

这个结构是 IMAGE_COR20_HEADER,定义如下:

typedef struct IMAGE_COR20_HEADER
{
    // Header versioning
    DWORD                   cb;              
    WORD                    MajorRuntimeVersion;
    WORD                    MinorRuntimeVersion;

    // Symbol table and startup information
    IMAGE_DATA_DIRECTORY    MetaData;        
    DWORD                   Flags; // 这个字段的意义,参考 ReplacesCorHdrNumericDefines           

    // The main program if it is an EXE (not used if a DLL?)
    // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint.
    // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint
    // (depricated for DLLs, use modules constructors intead). 
    union {
        DWORD               EntryPointToken;
        DWORD               EntryPointRVA;
    };

    // This is the blob of managed resources. Fetched using code:AssemblyNative.GetResource and
    // code:PEFile.GetResource and accessible from managed code from
    // System.Assembly.GetManifestResourceStream.  The meta data has a table that maps names to offsets into
    // this blob, so logically the blob is a set of resources. 
    IMAGE_DATA_DIRECTORY    Resources;
    // IL assemblies can be signed with a public-private key to validate who created it.  The signature goes
    // here if this feature is used. 
    IMAGE_DATA_DIRECTORY    StrongNameSignature;

    IMAGE_DATA_DIRECTORY    CodeManagerTable;            // Depricated, not used 
    // Used for manged codee that has unmaanaged code inside it (or exports methods as unmanaged entry points)
    IMAGE_DATA_DIRECTORY    VTableFixups;
    IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;

    // null for ordinary IL images.  NGEN images it points at a code:CORCOMPILE_HEADER structure
    IMAGE_DATA_DIRECTORY    ManagedNativeHeader;

} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;

其中,Flags 的值可以参考如下枚举:

typedef enum ReplacesCorHdrNumericDefines
{
// COM+ Header entry point flags.
    COMIMAGE_FLAGS_ILONLY               =0x00000001,
    COMIMAGE_FLAGS_32BITREQUIRED        =0x00000002, 
    COMIMAGE_FLAGS_IL_LIBRARY           =0x00000004,
    COMIMAGE_FLAGS_STRONGNAMESIGNED     =0x00000008,
    COMIMAGE_FLAGS_NATIVE_ENTRYPOINT    =0x00000010,
    COMIMAGE_FLAGS_TRACKDEBUGDATA       =0x00010000,
    COMIMAGE_FLAGS_32BITPREFERRED       =0x00020000,

// 省略一些无关的内容
} ReplacesCorHdrNumericDefines;

说明:以上定义可以在 CorHdr.h 中找到。

了解了以上知识,就可以手动查看 PE 文件来进行判断了。但是手动判断既容易错,又麻烦,还得时不时得翻看一下 PE 文件格式,很不方便。除了通过手动查看 PE 文件来查看,还可以通过工具来查看。本文简单介绍几个常用工具及其查看方法。

查看工具

  • CorFlags.exe

    view-net-bitness-in-cff-explorer.png

    除了查看,CorFlags.exe 也可以修改对应的标记位。具体用法可以直接在命令行中输入 CorFlags.exe 进行查看。

  • dumpbin

    dumpbin 可以查看很多信息,对于 .net 程序,可以使用 dumpbin /clrheader 选项查看 clr 头信息。如下图:
    view-net-bitness-in-dumpbin.png

    上图是两个以不同编译选项生成的程序的对比效果,我第一次查看 dumpbin 的显示结果没看懂,对比后才明白。

  • cff explorer

    带图形界面的 PE 工具,不仅可以查看,也可以修改,很方便。
    view-net-bitness-in-corflags.png

除了这几个工具,还有很多其它工具也可以查看,就不一一列举了。

参考资料

相关文章
|
1月前
|
XML 存储 安全
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
55 0
|
2月前
|
Ubuntu 持续交付 API
如何使用 dotnet pack 打包 .NET 跨平台程序集?
`dotnet pack` 是 .NET Core 的 NuGet 包打包工具,用于将代码打包成 NuGet 包。通过命令 `dotnet pack` 可生成 `.nupkg` 文件。使用 `--include-symbols` 和 `--include-source` 选项可分别创建包含调试符号和源文件的包。默认情况下,`dotnet pack` 会先构建项目,可通过 `--no-build` 跳过构建。此外,还可以使用 `--output` 指定输出目录、`-c` 设置配置等。示例展示了创建类库项目并打包的过程。更多详情及命令选项,请参考官方文档。
191 11
|
2月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
|
2月前
|
自然语言处理 C# 图形学
使用dnSpyEx对.NET Core程序集进行反编译、编辑和调试
使用dnSpyEx对.NET Core程序集进行反编译、编辑和调试
|
4月前
|
Linux C# iOS开发
如何用 WinDbg 调试Linux上的 .NET程序
【7月更文挑战第13天】 1. `dotnet-dump`: Collects process dumps with `dotnet-dump collect -p <process_id>`. 2. `lldb`: Debugs Mono runtime apps on macOS/Linux. 3. **Visual Studio Code**: Remotely debugs .NET via the C# extension. 4. **JetBrains Rider**: Supports remote debugging of .NET on Linux.
|
3月前
|
开发框架 NoSQL .NET
使用 Asp.net core webapi 集成配置系统,提高程序的灵活和可维护性
使用 Asp.net core webapi 集成配置系统,提高程序的灵活和可维护性
|
3月前
|
Web App开发 数据采集 开发框架
在.NET程序中整合微软的Playwright,使用 Playwright 的最佳实践和技巧
在.NET程序中整合微软的Playwright,使用 Playwright 的最佳实践和技巧
|
5月前
|
存储 安全 C#
技术心得记录:强命名的延迟与关联在.net程序集保护中的作用及其逆向方法
技术心得记录:强命名的延迟与关联在.net程序集保护中的作用及其逆向方法
|
5月前
|
开发框架 前端开发 JavaScript
程序与技术分享:ASP.NET发展史(【译】)
程序与技术分享:ASP.NET发展史(【译】)
49 0
|
5月前
|
网络协议
技术好文:Smark.Net实现简单聊天程序
技术好文:Smark.Net实现简单聊天程序
36 0

热门文章

最新文章