MSIL解析一(转)
转自:http://www.cnblogs.com/Yahong111/archive/2007/08/15/857140.html
在网上发现了一个非常好的MSIL教程,可惜是英文版的,于是就翻译了一下,与大家共享,
原文http://www.codeguru.com/Csharp/.NET/net_general/il/article.php/c4635,初次翻译文章,请大家指正。
介绍
微软中间语言 (MSIL) 是一种语言,是许多编译器(C#,VB.NET等)的输出. ILDasm (中间语言反汇编器)程序和.Net Framework SDK(FrameworkSDK\Bin\ildasm.exe)打包在一起,让用户以人可阅读的格式查看MSIL代码。通过该工具,我们可以打开任何.net可执行文件(exe或dll)并查看其MSIL代码。
ILAsm 程序(中间语言编译器)从MSIL语言生成可执行文件。我们可以在WINNT \ Microsoft.NET \Framework \vn.nn.nn目录中找到这个程序。
许多Visual C++程序员开始.net开发是因为他们对.NET框架的底层发生了一些什么感兴趣。学习MSIL给了用户理解某些对C#程序员或VB.NET程序员来说是透明的东西的机会。通晓MSIL给.NET程序员更多的能力。我们从不需要直接用MSIL编写程序,但是在某些情况下是非常有用的,我们可以用ILDasm打开程序的MSIL代码,查看它到底做了一些什么。
一个Doc格式的MSIL参考对.NET开发人员来说比较有用,它也许可以在Framework SDK目录下找到:
- FrameworkSDK\Tool Developers Guide\docs\Partition II Metadata.doc (元数据定义和术语). 在这个文件中,我发现了所有MSIL指令的说明,例如.entrypoint, .locals等.
- FrameworkSDK\Tool Developers Guide\docs\Partition III CIL.doc (CIL命令集)包含了一个MSIL命令的完整列表。
在工作中,我也用到了一个MSDN的ILDAsm教程,一篇2001年5月由John Robbins发表在MSDN杂志的优秀的文章:"ILDASM is Your New Best Friend"。
我想学习一门语言最好的途径就是用它写一些程序,所以我决定写一些小的MSIL程序。实际上,我们有写这些代码——是C#编译器生成的,我只是做一了一些小的更改,并加了许多注释以描述MSIL是如何工作的。
通过阅读附在本文的例子可以帮助.NET程序员理解中间语言,帮助其在需要的时候更易读懂MSIL代码。
一般信息
在MSIL中,所有的操作都在栈上完成。当调用一个函数的时候,其参数和局部变量都被分配到栈上。函数的代码从该栈开始,把一些值压入栈,对这些值进行一些操作,从栈上取出值。
执行MSIL名利和函数由3个步骤完成:
1. 把命令操作数和函数参数压入栈。
2. 执行命令或者调用函数。命令或函数从栈中取出他们的操作数(参数)并把他们压入结果栈 (返回值)。
3. 从栈中读取结果值。
步骤1~3是可选的,例如,void函数不会压入一个结果值到栈。
栈包含值类型对象和引用类型对象的引用。引用类型对象本身保存在堆中。
用来把一个值压入栈中的MSIL命令是ld... (装载),用来从栈中取出值的命令是st... (存储),因为值都存在变量中。我们可以把入栈操作叫做装载,出栈操作叫做存储。
示例项目
本文附上的代码中包含了许多用MSIL写的控制台程序. 如果需要编译他们,请确定ILAsm程序可以通过PATH访问。每个项目都是一个Visual Studio解决方案,IL源文件可以用VS的文本编辑器打开,Build命令运行ILAsm 程序在项目所在目录生成exe文件,run命令执行该文件。在每个程序的末尾,我加了几行代码,他们可以用C#来写:
Console.WriteLine("Press Enter to continue");
Console.Read();
这样,当从Windows Explorer运行的时候,就可以看到程序的输出。
下面是所含项目的列表:
1. 打印字符串—打印字符传到控制台。
2. 赋值—给一个int变量赋值并把它打印到控制台。
3. 运算—从控制台读取2个数字,惊醒+,-和乘的操作,并显示结果。
4. 数组— 分配一个int类型的数组,给他的元素赋值,打印其元素和数组的长度。
5. 比较— 输入2个数字并打印出最小的那个。
6. 数组2— 用循环填充数组元素并打印某些元素。
7. 不安全代码— 使用unsafe指针访问数组元素。
8. PInvoke— 调用Win32 API。
9. 类— 和类一起工作。
10. 异常— 异常处理。
我假设你以在这所说的顺序阅读这些项目。在下面的项目描述中,我用程序来解释每一条MSIL命令,并给出一些代码片段。
打印字符串
PrintString 就是MSIL版的 Hello, World
在代码中用到的MSIL指令如下:
l .entrypoint— 定义程序的入口点(该函数在程序启动的时候由.NET 运行库调用)
l .maxstack— 定义函数代码所用堆栈的最大深度。C#编译器可以对每个函数设置准确的值, 在例子中,我把他设为8。
用到的MSIL命令如下:
- ldstr string—把一个字符串常量装入堆栈。
- call function(parameters)—调用静态函数。函数的参数必须在函数调用前装入堆栈。
- pop— 取出栈顶的值。当我们不需要把值存入变量时使用。
- ret— 从一个函数中返回。
调用静态函数很简单。我们把函数的参数压入堆栈,调用函数,然后从堆栈中读取函数的返回值(如果是非void函数)。Console.WriteLine 就是一个这样的函数。
下面是代码:
.assembly PrintString {}
/*
Console.WriteLine("Hello, World)"
*/
.method static public void main() il managed
{
.entrypoint // 该函数是程序的入口
.maxstack
// *****************************************************
// Console.WriteLine("Hello, World)";
// *****************************************************
ldstr "Hello, World" // 把字符串压入堆栈
// 调用静态的System.Console.Writeline函数
// (函数移除栈顶的字符串)
call void [mscorlib]System.Console::WriteLine
(class System.String)
// *****************************************************
ldstr "Press Enter to continue"
call void [mscorlib]System.Console::WriteLine
(class System.String)
// 调用 System.Console.Read 函数
call int32 [mscorlib]System.Console::Read()
// pop 指令移除栈顶元素
// (移除由Read()函数返回的数字
pop
// *****************************************************
ret
}
赋值
该程序给一个变量赋与int值并把它打印到控制台窗口。
命令:
- ldc.i4.n—把一个 32位的常量(n从0到8)装入堆栈
- stloc.n— 把一个从堆栈中返回的值存入第n(n从0到8)个局部变量
代码:
.assembly XequalN {}
// int x;
// x = 7;
// Console.WriteLine(x);
.method static public void main() il managed
{
.entrypoint
.maxstack
.locals init ([] int32 x) // 分配一个局部变量
// *****************************************************
// x = 7;
// *****************************************************
ldc.i4. // 把常量装入堆栈
stloc. // 把堆栈中的值存入第0个变量
// *****************************************************
// Console.WriteLine(x);
// *****************************************************
ldloc. // 把第0个变量转入堆栈
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
数据运算
本程序从控制台读取2个数字,对它们进行简单的运算,然后显示结果。
命令:
- add—2个值相加。命令的参数必须在调用前装入堆栈,该函数从堆栈中移除参数并把运算后的结果压入堆栈。
- sub— 2个值相减。
- mul— 2个值相乘。
代码片段:
.assembly Operations {}
/*
// 程序的C#代码:
int x, y, z;
string s;
Console.WriteLine("Enter x:");
s = Console.ReadLine();
x = Int32.Parse(s);
Console.WriteLine("Enter y:");
s = Console.ReadLine();
y = Int32.Parse(s);
z = x + y;
Console.Write("x + y = ");
Console.Write(z);
Console.WriteLine("");
z = x - y;
Console.Write("x - y = ");
Console.Write(z);
Console.WriteLine("");
z = x * y;
Console.Write("x * y = ");
Console.Write(z);
Console.WriteLine("");
*/
.method static public void main() il managed
{
.entrypoint
.maxstack
.locals init ([] int32 x,
[] int32 y,
[] int32 z,
[] string s)
// *****************************************************
// Console.WriteLine("Enter x:");
// *****************************************************
ldstr "Enter x:" // 把字符装入堆栈
call void [mscorlib]System.Console::WriteLine(string)
// *****************************************************
// s = Console.ReadLine();
// *****************************************************
call string [mscorlib]System.Console::ReadLine()
stloc. // 把值存入第3个变量
// *****************************************************
// x = Int32.Parse(s);
// *****************************************************
ldloc. // 把第3个变量装入堆栈
// 调用 System.Int32::Parse(string)函数
// 把字符串从堆栈中移除并把解析的结果——int值压入堆栈
call int32 [mscorlib]System.Int32::Parse(string)
stloc. // 把值存入第0个变量
// *****************************************************
// 和变量y的一些运算
// *****************************************************
ldstr "Enter y:"
// 装入字符串
call void [mscorlib]System.Console::WriteLine(string)
// 调用
call string [mscorlib]System.Console::ReadLine()
// 调用
stloc.
//把值存入第3个变量
ldloc.
//把第3个变量装入堆栈
call int32 [mscorlib]System.Int32::Parse(string)
// 调用
stloc.
//把值存入第1个变量
// *****************************************************
// z = x + y;
// *****************************************************
ldloc. //把第0个变量装入堆栈
ldloc. //把第1个变量装入堆栈
// 把这2个值从堆栈中移除,把结果压入堆栈
add
stloc. //把值存入第2个变量
// *****************************************************
// Console.Write("x + y = ");
// *****************************************************
ldstr "x + y = " // load string onto stack
call void [mscorlib]System.Console::Write(string)
// *****************************************************
// Console.Write(z);
// *****************************************************
ldloc. //把第2个变量装入堆栈
call void [mscorlib]System.Console::Write(int32)
// *****************************************************
// Console.WriteLine("");
// *****************************************************
ldstr "" //装入字符串
call void [mscorlib]System.Console::WriteLine(string)
//相减和相乘运算过程与上面相同
ret
}
MSIL解析一(转)的更多相关文章
- Dotnet文件格式解析
0x0.序 解析过程并没有介绍对pe结构的相关解析过程,网上此类相关资料很多可自行查阅,本文只介绍了网上资料较少的从pe结构的可选头中的数据目录表中获取dotnet目录的rva和size,到完全解析d ...
- 解析Exception和C#处理Exception的常用方法总结
在.NET中,异常是指成员没有完成它的名称宣称可以完成的行动.在异常的机制中,异常和某件事情的发生频率无关. 异常处理四要素包括:一个表示异常详细信息的类类型:一个向调用者引发异常类实例的成员:调用者 ...
- [译]C++, Java和C#的编译过程解析
1.1.1 摘要 我们知道计算机不能直接理解高级语言,它只能理解机器语言,所以我们必须要把高级语言翻译成机器语言,这样计算机才能执行高级语言编写的程序,在接下来的博文中,我们将介绍非托管和托管语音的编 ...
- 写给自己的 程序集&msil 扫盲
嘴上不说 心里却想MD 这家伙在博客园装了这么久的高手 竟然连这都不会 ,我去噢. 程序集签名 .net 下 “程序集” 什么东东 ,反正就是听着挺牛x的,其实就是指“一堆程序”从我们传统的C++封装 ...
- C/C++, Java和C#的编译过程解析
原文地址:http://www.cnblogs.com/rush/p/3155665.html 1.1.1 摘要 我们知道计算机不能直接理解高级语言,它只能理解机器语言,所以我们必须要把高级语言翻译成 ...
- 深入解析.NET框架
一.AOP框架 Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种 ...
- OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现
最近趁着不忙,在构思一个搭建一个开源的完整项目,至于原因以及整个项目框架后边文章我再说明.既然要起一个完整的项目,那么数据仓储访问就必不可少,这篇文章我主要介绍这个新项目(OSS.Core)中我对仓储 ...
- C++, Java和C#的编译、链接过程解析
总是感觉java是解释性语言,转载下一篇感觉写的容易理解的文章 转自 http://www.cnblogs.com/rush/p/3155665.html 1.1.1 摘要 我们知道计算机不能直接理解 ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
随机推荐
- ASP.NET 5系列教程 (四):向视图中添加服务和发布应用到公有云
向视图中添加服务 现在,ASP.NET MVC 6 支持注入类到视图中,和VC类不同的是,对类是公开的.非嵌套或非抽象并没有限制.在这个例子中,我们创建了一个简单的类,用于统计代办事件.已完成事件和平 ...
- 注册asp.net
%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i
- paip.语义分析--单字词名词表
paip.语义分析--单字名词表 INSERT INTO t (word) SELECT DISTINCT word FROM `word_main` where tsisin is not n ...
- Leetcode 58 Length of Last Word 字符串
找出最后一个词的长度 class Solution { public: int lengthOfLastWord(string s) { , l = , b = ; while((b = s.find ...
- NGUI ScrollView 循环 Item 实现性能优化
今天来说说一直都让我在项目中头疼的其中一个问题,NGUI 的scrollView 列表性能问题,实现循环使用item减少性能上的开销. 希望能够给其他同学们使用和提供一个我个人的思路,这个写的不是太完 ...
- iOS开发Swift篇(01) 变量&常量&元组
iOS开发Swift篇(01) 变量&常量&元组 说明: 1)终于要写一写swift了.其实早在14年就已经写了swift的部分博客,无奈时过境迁,此时早已不同往昔了.另外,对于14年 ...
- 用SDWebImage渐变加载图片
用SDWebImage渐变加载图片 使用 使用请详细查看源码,只需要给定一个图片地址以及一个placeHolder图片(非必须)即可. 效果 源码 https://github.com/YouXian ...
- JAVA “Run as administrator” “UAC disabled” alternative solution
Technorati 标签: psexec,run as administrator,UAC java.io.IOException: Cannot run program "psexec. ...
- [转] sql_id VS hash_value
有没有发现,v$session,v$sql,v$sqlarea,v$sqltext,v$sql_shared_cursor等试图连接的时候经常会用到hash_value,sql_id,但是他们2个之间 ...
- 单点登录 SSO(Single Sign-On)的实现原理
为什么要 SSO? 企业的信息化过程是一个循序渐进的过程,这就造成在企业的不同时期,根据业务和发展需要,构建了多个应用程序,而这些应用程序在功能.设计和技术可能都有所不同,就形成了各自独立的用户库和用 ...