惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
G
Google Developers Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
Cloudbric
Cloudbric
L
LINUX DO - 最新话题
MyScale Blog
MyScale Blog
H
Heimdal Security Blog
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
S
Security @ Cisco Blogs
Latest news
Latest news
I
Intezer
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
T
Threatpost
博客园 - 【当耐特】
S
Schneier on Security
P
Privacy International News Feed
G
GRAHAM CLULEY
T
Tenable Blog
AWS News Blog
AWS News Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
博客园 - Franky
Engineering at Meta
Engineering at Meta
美团技术团队
S
Secure Thoughts
T
Troy Hunt's Blog
Microsoft Security Blog
Microsoft Security Blog
SecWiki News
SecWiki News
V
Visual Studio Blog
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Martin Fowler
Martin Fowler
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - 永红

东莞依时利诚聘java与.net方面的英才 项目管理笔记(观念) 项目管理笔记(概述) 【置顶】DLPrinter WEB打印组件第2次更新 也谈WEB打印(五):在IE的打印预览窗口中定义自己的用户接口 也谈WEB打印(四):让我们的模板支持打印,并根据内容动态的生成页面 也谈WEB打印(三):抛开IE,实现我们自己的打印模板 也谈WEB打印(二):简单的分析一下IE的打印原理并实现简单的打印和预览 也谈WEB打印(-):目前的几种方式及我们的任务 整理的几个常用的数据库维护的脚本 揭秘SQL Server 2000中的Bookmark Lookup 用MSIL剥开C#的外衣(一):方法参数ref、out、params和lock、for和foreach关键字 【翻译】MSIL 教程(三):类和异常处理 【翻译】MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API 设计模式实战:用Prototype模式提高系统的开发效率,用DynamicMethod、Flyweight模式提高系统的运行效率 用DynamicMethod提升ORM系统转换业务数据的性能 设计模式实战:在WinForm中用Command模式实现可以撤销的数据操作 把Sql数据转换为业务数据的几种方法 设计模式实战:组合型模式Composite,Adapter
【翻译】MSIL 教程(一)
永红 · 2007-08-15 · via 博客园 - 永红

在网上发现了一个非常好的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可执行文件(exedll)并查看其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命令的完整列表。

在工作中,我也用到了一个MSDNILDAsm教程,一篇20015月由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 8

    // *****************************************************

    // 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位的常量(n08)装入堆栈
  • stloc.n把一个从堆栈中返回的值存入第nn08)个局部变量

代码:

.assembly XequalN {}

// int x;

// x = 7;

// Console.WriteLine(x);

.method static public void main() il managed

{

    .entrypoint

    .maxstack 8

    .locals init ([0] int32 x)  // 分配一个局部变量

    // *****************************************************

    // x = 7;

    // *****************************************************

    ldc.i4.7                    // 把常量装入堆栈

    stloc.0                     // 把堆栈中的值存入第0个变量

    // *****************************************************

    // Console.WriteLine(x);

    // *****************************************************

    ldloc.0                     // 把第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 8

    .locals init ([0] int32 x,

           [1] int32 y,

           [2] int32 z,

           [3] 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                     // 把值存入第3个变量

    // *****************************************************

    // x = Int32.Parse(s);

    // *****************************************************

    ldloc.3                     // 把第3个变量装入堆栈

    // 调用 System.Int32::Parse(string)函数

    // 把字符串从堆栈中移除并把解析的结果——int值压入堆栈

    call       int32 [mscorlib]System.Int32::Parse(string)

    stloc.0                     // 把值存入第0个变量

    // *****************************************************

    // 和变量y的一些运算

    // *****************************************************

    ldstr      "Enter y:"

               // 装入字符串

    call       void [mscorlib]System.Console::WriteLine(string)

               // 调用

    call       string [mscorlib]System.Console::ReadLine()

               // 调用

    stloc.3

               //把值存入第3个变量

    ldloc.3

               //把第3个变量装入堆栈

    call       int32 [mscorlib]System.Int32::Parse(string)

               // 调用

    stloc.1

               //把值存入第1个变量

    // *****************************************************

    // z = x + y;

    // *****************************************************

    ldloc.0             //把第0个变量装入堆栈

    ldloc.1             //把第1个变量装入堆栈

    // 把这2个值从堆栈中移除,把结果压入堆栈

add

    stloc.2             //把值存入第2个变量

    // *****************************************************

    // Console.Write("x + y = ");

    // *****************************************************

    ldstr      "x + y = "          // load string onto stack

    call       void [mscorlib]System.Console::Write(string)

    // *****************************************************

    // Console.Write(z);

    // *****************************************************

    ldloc.2                    //把第2个变量装入堆栈

    call       void [mscorlib]System.Console::Write(int32)

    // *****************************************************

    // Console.WriteLine("");

    // *****************************************************

    ldstr      ""                  //装入字符串

    call       void [mscorlib]System.Console::WriteLine(string)

    //相减和相乘运算过程与上面相同

    ret

}

未完待续。