Windows如何区分常规EXE和.NET exe?

31

在面试中,有人问我Windows操作系统如何区分常规exe和.NET exe。

我的回答是,在构建.NET exe时,编译器会将一些信息放入头文件中,该信息是PE32或PE32+。Windows通过验证头文件来确定是否需要加载MSCOREE.dll,然后加载CLR并执行EXE。

我的答案正确吗?


8
哇,那是一个难回答的面试问题。 - brydgesk
6
一道毫不留情的问题 ;) - Jhonny D. Cano -Leftware-
6
采访者可能在前一晚刚读了一本有关CLR或IL的书。 - Matthew Whited
2
如果这份工作不是用于编写底层代码,那么看起来你遇到了标准的无用大公司技术面试问题之一。这就像问一个列车司机火车引擎的活塞尺寸一样,与现实没有关系。 - Andrew Lewis
1
@KierenJohnstone:那是错误的。乍一看,你说的似乎是对的。但是如果您尝试修改EXE文件,您会发现当Windows OS检测到正在运行的应用程序是.NET应用程序时,它会采取完全不同的路线。 - Vahid Nasehi
显示剩余3条评论
4个回答

17

我认为以下两个链接是了解PE文件结构和Windows加载器的好资源。

我认为回答了你的问题的确切语句来自于2002年3月的文章:

 

.NET可执行文件的主要目的是将特定于.NET的信息(如元数据和中间语言(IL))加载到内存中。此外,.NET可执行文件链接到MSCOREE.DLL。该DLL是.NET进程的起点。当.NET可执行文件加载时,它的入口点通常是一个很小的代码存根。该存根只是跳转到MSCOREE.DLL中的导出函数(_CorExeMain或_CorDllMain)。 从那里开始,MSCOREE接管并开始使用来自可执行文件的元数据和IL。这种设置类似于Visual Basic(.NET之前)中应用程序使用MSVBVM60.DLL的方式。


@Doruk:我想我已经为你修复了那个链接。希望我没有让它变得更糟糕:o) - Matthew Whited
记录一下,自XP以来,Chris的答案更正确。 - argaz
2
这是更好的答案:https://dev59.com/gmw15IYBdhLWcg3wcbay - Sam

6

我不记得你说的是否正确,但听起来是对的。希望上面的链接能有所帮助。 - Matthew Whited
我曾在Jeff Richter的书《CLR via C#》中读到过这个。那是一段时间之前的事了,我只能模糊地记得。感谢你们所有人发布答案和链接。 - AlwaysAProgrammer
很遗憾,你的两个链接在'16年中无法使用 :( - MaLiN2223

5
简而言之,对于XP及以上版本,操作系统加载程序已经被增强以便根据PE目录项检测托管程序集。如果目录项存在,则加载器自动加载mscoree.dll,并跳转到mscoree中的一个函数_CorExeMain(2)(对于可执行文件)或_CorDllMain(对于dll)。_CorExeMain负责加载CLR并启动托管代码的执行。
我使用以下内容来提醒自己入口点名称...
C:\Windows\System32>dumpbin -exports mscoree.dll
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file mscoree.dll

File Type: DLL

  Section contains the following exports for mscoree.dll

    00000000 characteristics
    4AF3AF84 time date stamp Fri Nov 06 07:09:24 2009
        0.00 version
          17 ordinal base
         126 number of functions
         123 number of names

    ordinal hint RVA      name

         38    0 0001AAA0 CLRCreateInstance
... Lots of stuff left out...
        136   76 00015030 _CorDllMain
        138   77 00004DDB _CorExeMain
        137   78 0001A981 _CorExeMain2
        139   79 0002033B _CorImageUnloading
        140   7A 000042D0 _CorValidateImage
         24      00008017 [NONAME]
        142      00014C4D [NONAME]

  Summary

        4000 .data
        4000 .reloc
        1000 .rsrc
       40000 .text

这并不完全正确。就 Windows 而言,它不知道它正在加载一个 .NET 可执行文件。它只知道它正在加载一个本地二进制文件以及一些依赖的 DLL。可执行文件的入口点是一个引导程序,它会跳转到 mscoree.dll,然后启动 CLR。 - Sam
@ChrisTaylor 嗯,真的吗?你有相关的参考资料吗?这可能可以解释我正在处理的某些东西中出现的一些奇怪问题。 - Sam
1
@Sam,我为你快速谷歌了一下,下面的链接是我能在短时间内找到的许多参考资料之一。它提到了ntdll.dll中的Windows模块加载器检测到正在加载.NET可执行文件并采取特殊操作。https://books.google.com/books?id=GbNCAwAAQBAJ&pg=PT68&lpg=PT68&dq=how+windows+loads+a+.net+executable&source=bl&ots=mpyMZlTzzZ&sig=ZO-tAKQg8CgD4NQ_TsMVttXaYyk&hl=en&sa=X&ved=0CEcQ6AEwB2oVChMIi5rVvsTQxwIVhRk-Ch2ROgOr#v=onepage&q=how%20windows%20loads%20a%20.net%20executable&f=false - Chris Taylor
非常感谢!我一直在谷歌搜索,但还没有找到具体的内容,只是提到“XP及以后不会这样做”。这就解释了为什么.NET可执行文件中PE头中的入口点似乎从未被执行。我想这仍然指向跳转到“_CorExeMain”的存根以实现向后兼容性。再次感谢,我已经花了大部分时间来弄清楚这个问题! - Sam
1
当然现在我知道我在寻找什么,这是显而易见的。对于未来的搜索者,这里的文档详细说明了这种行为:https://msdn.microsoft.com/en-us/library/4ce9k7xb(v=vs.110).aspx - Sam
显示剩余4条评论

2

这是一个棘手的问题。你得到了这份工作吗?;-)

我知道已经过了一段时间,但对于任何寻找答案的人:

答案可以在MSDN中找到:https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/corvalidateimage-function

在Windows XP和更高版本中,操作系统加载程序通过检查通用对象文件格式(COFF)头中的COM描述符目录位来检查托管模块。设置的位表示托管模块。如果加载程序检测到托管模块,则加载MsCorEE.dll并调用_CorValidateImage...

您可以使用 dumpbin /clrheader 自行检查(原生模块将为空)。

如果您考虑一下(至少在.NET4+中),这必须在进程启动之前由加载程序完成(这意味着您不能等待模块调用CLR初始化例程),因为AnyCPU表示加载程序动态确定进程的位数,在进程启动之前必须明确知道。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接