net IL的一些探索
查看IL有2个工具比较好用,一个是大名鼎鼎的Reflector,但已经转向收费,另一个是开源的ILSpy,强大好用,对linq和lambda表达示的支持很好。相关的书籍也很多,比如这本Microsoft.NET IL汇编语言程序设计。看明白IL有一个关键的地方:IL是基于堆栈的语言,类似汇编,每条指令都会从栈顶pop弹出需要用到的变量(if any),然后再把返回值(if any)push压入堆栈。记住这一点,对看懂IL非常有帮助。
1、先说下这东西有什么用
比如关于i++的一个老问题:在C#里下面这段程序的输出是什么?
int i = 0;
for(int j=0;j<3;j++){
i = i++;
Console.Write("i={0} ", i);
}
和java一样,这里会输出 i=0 i=0 i=0
,为什么呢?从IL里就能一目了然:
.locals init (
[0] int32 i,
[1] int32 j,
[2] bool CS$4$0000
)
//for 循环的IL代码不贴了,关键是`i=i++`的部分如下:
IL_0008: ldloc.0
IL_0009: dup //把i复制了一份
IL_000a: ldc.i4.1
IL_000b: add
IL_000c: stloc.0 //把++后的值赋给i
IL_000d: stloc.0 //把复制的原值又重新赋给i
换句话说,编译器是这样来执行i=i++
的:tmp=i; i=i+1; i=tmp;
,所以会一直输出i=0
。如果换成i=++i
,那么编译器会转化成tmp=i+1; tmp1=tmp; i=tmp1; i=tmp;
,当然就能正常输出i=1 i=2 i=3
了。另外,对于string+string与StringBuilder的性能对比、多线程的对共享变量的争用(你以为的一条C#语句,实际是多条汇编语句、并且CPU还可能优化调整语句的执行顺序),IL代码也有很强的指导意义,这里暂不展开了。
2、再来看一段稍复杂的c#源码,详细了解一下IL
这段代码是用来做AES加密的,如下
public static string Encrypt(string strEncrypt, string strKey){
try{
byte[] keyArray = Encoding.UTF8.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(strKey, "md5"));
byte[] strEncryptArray = Encoding.UTF8.GetBytes(strEncrypt);
byte[] resultArray = null;
using(RijndaelManaged rDel = new RijndaelManaged()){
rDel.Key = keyArray;
rDel.Mode = CipherMode.ECB;
rDel.Padding = PaddingMode.PKCS7;
resultArray = rDel.CreateEncryptor().TransformFinalBlock(strEncryptArray, 0, strEncryptArray.Length);
}
}
catch {
return null;
}
}
3、对应的IL及解释如下
.method public hidebysig static string Encrypt(string strEncrypt, string strKey) cil managed{
//hidebysig表示该方法会覆盖父类对应的方法,并且只是提供给Compiler的一个Flag,运行时用不到这个标志位。我理解被hidebysig标记过的方法在C#里相当于加上new关键字
//cil是默认的标识位,表示这段代码采用Common Intermediate Language实现
//managed表示这是一段托管代码,也是默认的标识位
//代码大小 137(0x89)
.maxstack 4 //表示这个方法用到的最大栈深度,这里的4不是byte而是slot,每个slot代表一个占位,不论item大小都占一个slot。呆会儿走完这个方法,我们可以数一下最大栈深是不是4?
.locals init ([0] uint8[] keyArray, //按顺序声明本地变量,uint对应byte
[1] uint8[] strEncryptArray,
[2] uint8[] resultArray,
[3] class [mscorlib]System.Security.Crytography.RijndaelManaged rDel,
[4] class [mscorlib]System.Security.Crytography.ICryptoTransform cTransform, //rDel.CreateEncryptor()生成的中间变量
[5] string CS$1$0000, //返回值的临时变量
[6] bool CS$4$0001) //判断是否应该释放using里生成的rDel的临时变量
IL_0000: nop
.try {
IL_0001: nop
IL_0002: call class[mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_UTF8() //对应Encoding.UTF8,这也反映了属性即方法
IL_0007: ldarg.1 //将arg.1也就是传入方法的第二个变量strKey压栈,以备下面调用时出栈
IL_0008: ldstr "md5" //同理,将字符串"md5"压栈,这时最大栈深=3(UTF8、arg.1、"md5")
IL_000d: call [System.Web]System.Web.Security.FormsAuthentication::HashPasswordForStoringInConfigFile(string, string) //调用方法,按照方法签名弹出栈里的2个slot,即最顶上的2个变量,并且隐含调用完将返回值压栈的操作
IL_0012: callvirt instance uint8[] [mscorlib]System.Text.Encoding::GetBytes(string) //将上一个方法压栈的string出栈,调用虚方法,将结果byte[]压栈,下面的方法调用都是按照出栈、压栈的顺序执行的,不一一赘述
IL_0017: stloc.0 //出栈,将上面返回的byte[]存入loc.0,即keyArray
……
IL_0026: newobj instance void [mscorlib]System.Security.Crytography.RijndaelManaged::.ctor() //newobj实例化
.try { //using在编译器中就是一个try、finally的简写,这里得到充分证明
IL_002c: nop //debug模式下,会生成这些nop操作,便于调试时在“{”处设置断点
……
IL_0036: ldc.i4.2 //压栈常量2,因为rDel.Mode=CipherMode.ECB;里面,ECB在枚举CipherMode中的值为2,可以看到enum在IL里就是数字常量
……
IL_005a: nop
IL_005b: leave.s IL_006f //跳出try语句,并且在leaving的时候,执行finally语句
} //end .try
finally {} //这里是编译器为using自动生成的语句,判断rDel是否为null,若不为null,调用其自身的Dispose()方法
……
IL_0086: ldloc.s CS$1$0000
IL_0088: ret //将返回值压栈后退出
} //end of method Encrypt
net IL的一些探索的更多相关文章
- 《你必须知道的.NET》读书笔记:从Hello World认识IL
通用的语言基础是.NET运行的基础,当我们对程序运行的结果有异议的时候,如何透过本质看表面,需要我们从底层来入手探索,这时候,IL便是我们必须知道的基础. 一.IL基础概念 1.1 什么是IL? IL ...
- Nmap备忘单:从探索到漏洞利用 Part1
在侦查过程中,信息收集的初始阶段是扫描. 侦查是什么? 侦查是尽可能多的收集目标网络的信息.从黑客的角度来看,信息收集对攻击非常有帮助,一般来说可以收集到以下信息: 电子邮件.端口号.操作系统.运行的 ...
- 学习微软中间语言(MSIL)的绝佳工具 Dotnet IL Editor 推荐
Dotnet IL Editor是一款.NET平台反编译工具,可以反编译.NET程序集文件为IL代码,并且可以执行,调试反编译后生成的IL代码.它的设计出发点比较直观,新建一个项目,添加程序集文件,设 ...
- 认识元数据和IL(下)<第五篇>
书接上回: 第二十四回:认识元数据和IL(上) , 第二十五回:认识元数据和IL(中) 我们继续. 终于到了,说说元数据和IL在JIT编译时的角色了,虽然两个回合的铺垫未免铺张,但是却丝毫不为过,因为 ...
- 认识元数据和IL(上) <第三篇>
说在,开篇之前 很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有<第一回:恩怨情仇:is和as>那么 ...
- 认识IL代码---从开始到现在 <第二篇>
·IL代码分析方法 ·IL命令解析 ·.NET学习方法论 1.引言 自从『你必须知道.NET』系列开篇以来,受到大家很多的关注和支持,给予了anytao巨大的鼓励和动力.俱往昔,我发现很多的园友都把目 ...
- 深入探索.NET框架内部了解CLR如何创建运行时对象
原文地址:http://msdn.microsoft.com/en-us/magazine/cc163791.aspx 原文发布日期: 9/19/2005 原文已经被 Microsoft 删除了,收集 ...
- Dotnet全平台下APM-Trace探索
背景 随着支撑的内部业务系统越来越多,向着服务化架构进化,在整个迭代过程中,会逐渐暴露出以下问题. 传统依赖于应用服务器日志等手段的排除故障原因的复杂度越来越高,传统的监控服务已经无法满足需求. 终端 ...
- C# 字符串拼接性能探索
本文通过ANTS Memory Profiler工具探索c#中+.string.Concat.string.Format.StringBuilder.Append四种方式进行字符串拼接时的性能. 本文 ...
随机推荐
- boost中的智能指针
进行本地线程管理的 thread_specific_ptr 指针: 可以看这里:http://www.kingofcoders.com/viewNews.php?type=newsCpp&id ...
- linux内核的熵池
也可以看百度科 Linux内核采用熵来描述数据的随机性.熵(entropy)是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大.在信息学中,熵被用来表征一个符号或 ...
- JavaSE复习_12 Socket网络编程
△客户端使用Scanner与BufferedReader的异同,Scanner在客户端调用s.shutdownoutput的时候,将会因为读不到行而报异常,但是BufferedReader的readl ...
- Android Contacts (android通讯录读取)-content provider
Content Provider 在数据处理中,Android通常使用Content Provider的方式.Content Provider使用Uri实例作为句柄的数据封装的,很方便地访问地进行数据 ...
- nginx系统真正有效的图片防盗链完整设置详解
原文:http://www.wufangbo.com/nginx-fang-dao-lian/ 关于nginx防盗链的方法网上有很多教程,都可以用,但是我发现很多教程并不完整,所做的防盗链并不是真正的 ...
- 修改jsp文件tomcat发布失败(Could not delete May be locked by another process)
突然项目修改jsp文件后,tomcat不能发布, Publishing failed with multiple errors Could not delete D:/Tomcat 6.0/web ...
- lftp
linux安装FTP工具 lftp及使用教程 来源:网络 发布时间:2013-05-24 15:21 字体:[大 中 小] 点击2510次 linux下可以直接通过FTP命令进行ftp上传下载,不 ...
- Linux下数据库的安装和使用
数据库有多重要就不用说了,每一个计算机相关行业的人都必须要学会基本的数据库操作,因为你总会用到的. 之前转过一些学习资源: 与MySQL的零距离接触 - 慕课网 Python操作MySQL数据库 生物 ...
- WebDriver 在使用 CSS Selector 与 XPath 在查找元素时如何取舍
开发在做Web系统时,用的是css div划分层,使用jQuery 选取元素.
- 在centos6.5中安装mysql5.7
简介 博主最近在研究mysql的读写分离和主从复制,一台master和两台slave,三台机器在同一个局域网中,首先就就要在centos6.5中安装mysql5.7.好了,废话不多说,接下来进入正题. ...