你的C#代码是怎么跑起来的(一)

写了那么多C#代码,大家有没有想过自己写的代码编译后的可执行文件内部是什么样子,是怎样在系统上运行的?

编译成exe,然后双击exe文件运行,这中间到底发生了些什么呢,这篇先来剖析下exe内部的样子:

我们知道C#代码编译后的结果是IL(Intermediate Language),那生成的exe文件里面都是IL吗,当然不会。

C#生成的exe既然是window下可执行文件,那也就是标准的PE文件,和普通win32的exe文件格式一样。我们来看下exe文件的格式:

dll文件本质上和exe一样,只是少了入口函数。

MS-DOC MZ Header和MS-DOS Stub是为了兼容DOS系统存在的,目的是使这个exe在DOS下执行时弹出一个提示"This program cannot be run in DOS mode"。

PE Header包含了这个文件的一些信息,如:文件创建日期,文件类型,Section的数量,Optional Header的大小等等。详细可以参考Winnt.h里的结构_IMAGE_FILE_HEADER。

PE Optional Header则包含了文件的版本号以及重要的基地址和AddressOfEntryPoint(RVA-Relative Virtual Address),这是程序执行的入口地址,双击exe后就从这里开始执行。对C#程序来说,这里指向的是.net的核心库MsCorEE.dll的_CorExeMain()函数。当然这是针对XP系统的,XP以后的系统,OS Loader已经可以判断出这个PE是否包含CLR头来决定是否运行MsCorEE.dll的_CorExeMain()函数。

Section有很多,包括代码节,数据节等,C#程序会把CLR头,元数据,IL放在这里面。

CLR是什么呢,全称Common Language Runtime,公共语言运行时,CLR主要是管理程序集,托管堆内存,异常处理和线程同步等等。

CLR头具体可以参考CorHdr.h中的IMAGE_COR20_HEADER结构,如下:

 1 typedef struct IMAGE_COR20_HEADER
 2     {
 3         // CLR版本信息
 4         ULONG cb;
 5         USHORT MajorRuntimeVersion;
 6         USHORT MinorRuntimeVersion;
 7 
 8         IMAGE_DATA_DIRECTORY MetaData; //元数据
 9         ULONG Flags;
10         ULONG EntryPointToken;  //入口函数Main的标识
11 
12 
13         IMAGE_DATA_DIRECTORY Resources;  //资源
14         IMAGE_DATA_DIRECTORY StrongNameSignature;  //强名称标识
15 
16 
17         // Regular fixup and binding information
18         IMAGE_DATA_DIRECTORY CodeManagerTable;
19         IMAGE_DATA_DIRECTORY VTableFixups;
20         IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
21 
22         // Precompiled image info (internal use only - set to zero)
23         IMAGE_DATA_DIRECTORY ManagedNativeHeader;
24 
25     }
26     IMAGE_COR20_HEADER;

元数据很重要,验证代码类型安全,GC的对象引用跟踪还有我们常用的反射都需要用到元数据。

元数据主要由定义表,引用表,清单表组成。

定义表包括应用所有的类型,方法,字段,属性,参数,事件的定义,代码里任何的定义项都可以在这个表里找到,反射就是靠这个表只要一个名字就能得到属性或函数。运行时的类型安全检查也离不开它。

引用表包括程序集,类型和成员的引用,我们知道GC在回收内存时先默认认为所有对象都是垃圾,然后通过线程栈上的根(cpu寄存器,局部变量,参数,静态变量)找引用的对象,能找到的说明还在使用就去掉垃圾标记,这个表可以让GC在回收内存时方便从根找到所有引用。

清单表主要是程序集,文件,资源的定义。

IL就不多说了,不了解的朋友可以参考小弟的另一篇文章:30分钟?不需要,轻松读懂IL

元数据和IL都可以通过工具ildasm.exe来查看。

以上就是C#生成的exe文件的主要结构,下篇再讲exe文件的运行过程,谢谢。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏wOw的Android小站

[设计模式]之六:桥接模式

这个原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封禁,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大...

270
来自专栏Linyb极客之路

为什么给你设置重重障碍?讲一讲Web开发中的跨域

因为在web交互的环境中,只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

834
来自专栏Android群英传

Android Native Crash 收集

本文是『张涛的NDK之旅』,本来很早以前就有很多读者希望我能写一些关于MDK的文章,但是由于我本身对NDK不熟悉,所以找来了同事张涛的文章。欢迎大家关注他的博客...

521
来自专栏小曾

通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core?

什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先从类型系统开始讲起,我将通过跨语言操作这个例子来逐...

1032
来自专栏蓝天

redis的一些简介

Redis是Remote Dictionary Server的缩写,他本质上一个Key/Value数据库,与Memcached类似的NoSQL型数据库。

611
来自专栏大内老A

通过扩展改善ASP.NET MVC的验证机制[使用篇]

ASP.NET MVC提供一种基于元数据的验证方式是我们可以将相应的验证特性应用到作为Model实体的类型或者属性/字段上,但是这依然具有很多的不足。在这篇文章...

1595
来自专栏kl的专栏

skywalking源码分析之javaAgent工具ByteBuddy的应用

关于skywalking请看我上一篇博文,其使用javaAgent技术,使得应用接入监控0耦合。今天在分析skywaking过程中,对javaAgent技术有了...

4688
来自专栏Linux驱动

6.分析request_irq和free_irq函数如何注册注销中断(详解)

上一节讲了如何实现运行中断,这些都是系统给做好的,当我们想自己写个中断处理程序,去执行自己的代码,就需要写irq_desc->action->handler,然...

5699
来自专栏FreeBuf

恶意程序分析利器PowerShellArsenal

简介 PowerShellArsenal是一个PowerShell模块,它的功能是帮助逆向工程师来分析.NET恶意软件,PowerShellArsenal的功能...

2309
来自专栏黑白安全

DLL注入新姿势:反射式DLL注入研究

在分析koadic渗透利器时,发现它有一个注入模块,其DLL注入实现方式和一般的注入方式不一样。搜索了一下发现是由HarmanySecurity的Stephen...

743

扫码关注云+社区