MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API

2024-04-17 01:38

本文主要是介绍MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

数组
本程序分配一个int型的数组并给他的元素赋值,然后打印出元素和数组的长度。
命令:
  • newarr type生成一个元素类型为type 的数组。数组的大小必须在调用该命令前装入堆栈。该命令会把一个数组的引用装入堆栈。
  • stelem.i4给一个数组成员赋值。数组的引用、下标和值必须在调用该命令前装入堆栈。
  • ldelema type把数组元素的地址装入堆栈。数组的引用和下标必须在调用该命令前装入堆栈。地址用来调用非静态函数(参见后面)。
  • ldlen把数组的长度装入堆栈。数组的引用必须在调用该命令前装入堆栈。
  • ldloca.s variable把变量的地址装入堆栈。
  • ldc.i4.s value把一个Int32的常量装入堆栈(用于大于8位的数)。
  • conv.i4把堆栈中值转换成Int32类型。
  • call instance function(arguments)调用类的非静态函数。在调用一个非静态函数之前,我们必须把某个类的实例的地址和函数的参数装入堆栈。在本例中,地址通过ldelemaldloca 命令装入。
在本例的某些代码片段中,我写了一些注释,以说明堆栈在最后一个变量后的状态。在本例中,我们看到变量由编译器生成,该变量用来调用类的非静态函数。
代码:
.assembly Array1 {}
 
/*
// This program works as C# code:
 
int[] x = new int[5];
x[0] = 10;
x[1] = 20;
 
Console.WriteLine("x[0] = " + x[0].ToString());
Console.WriteLine("x[1] = " + x[1].ToString());
Console.WriteLine("Array length = " + x.Length.ToString());
*/
 
.method static public void main() il managed
{
    .entrypoint
    .maxstack 8
 
    .locals init ([0] int32[] x,
                  [1] int32 tmp)    // 由编译器生成
 
    // *****************************************************
    // x = new int[5];
    // *****************************************************
    ldc.i4.5                     // 把常量装入堆栈。
 
    // 生成数组,并把他的引用压入堆栈
    newarr     [mscorlib]System.Int32
 
    // 把数组从堆栈中取出,存入第0个局部变量中
    stloc.0
 
    // *****************************************************
    // x[0] = 10;
    // *****************************************************
    ldloc.0           // 把第0个局部变量装入堆栈(数组)
    ldc.i4.0          // 把常量0装入堆栈(下标)
    ldc.i4.s   10     // 把常量10装入堆栈(值)
    stelem.i4         // array[index] = value
 
    // 对数组的其余元素进行同样的操作……
 
    // ***************************************************
    // Console.WriteLine("x[0] = " + x[0].ToString());
    // ***************************************************
    ldstr      "x[0] = "            
                // 堆栈:"x[0] = " (堆栈由局部变量表示)
    ldloc.0                         // 把第0个变量装入堆栈
    ldc.i4.0                        // 把第1个变量装入堆栈
                // 堆栈: "x[0] = " -> x -> 0
    // 把元素的地址装入堆栈
    ldelema    [mscorlib]System.Int32
                // 堆栈: "x[0] = " -> 指向一个Int32的指针
                // 10
    // 调用实例函数System.Int32::ToString().
    call       instance string [mscorlib]System.Int32::ToString()
                // 堆栈: "x[0] = " -> "10"
    // 调用静态函数System.String::Concat(string, string)
    call       string [mscorlib]System.String::Concat
                                               ( string , string )
                // 堆栈: "x[0] = 10"
    // 调用静态函数 System.Console::WriteLine(string)
    call       void [mscorlib]System.Console::WriteLine( string )
                // 堆栈: 空
 
    // 对数组的其余元素进行同样的操作……
 
    // *****************************************************
    // Console.WriteLine("Array length = " + x.Length.ToString());
    // *****************************************************
    ldstr      "Array length = "  
                // 堆栈: "Array length = "
    ldloc.0     // 把第0个变量装入堆栈
                // 堆栈: "Array length = " -> x
    Ldlen        // 把数组的长度装入堆栈
                 // 堆栈: "Array length = " -> 5
    conv.i4      // 把栈顶的值转换为Int32,并把他装入堆栈
                // 堆栈: "Array length = " -> 5
    stloc.1      // 把刚才的值存入第1个局部变量(tmp)
                // 堆栈: "Array length = "
    ldloca.s   tmp    // 把变量tmp的地址装入堆栈
                 // 堆栈: "Array length = " -> &tmp
    call       instance string [mscorlib]System.Int32::ToString()
                // 堆栈: "Array length = " -> "5"
    call       string [mscorlib]System.String::Concat
                                       ( string , string )
                // 堆栈: "Array length = 5"
    call       void [mscorlib]System.Console::WriteLine( string )
                // 堆栈: 空
    ret
}

比较
本程序读取2个数字并打印其最小值。
命令:
  • bge.s label跳转至label 如果value1≥value 2. Values 1和 2 必须在调用本命令前装入堆栈。
  • br.s label跳转至label
  • box value type把一个值类型转成一个Object,并把该Object的引用装入堆栈。
本程序的装箱由如下C#程序引起: Console.WriteLine("{0:d}", z);
用这种形式就不会引起装箱: Console.WriteLine(z.ToString());.
代码:
.assembly Compare {}
/*
            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);
 
            if ( x < y )
                z = x;
            else
                z = y;
 
            Console.WriteLine("{0:d}", z);
*/
 
.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个变量装入堆栈
    call       int32 [mscorlib]System.Int32::Parse( string )
    stloc.0                             // 保存到第0个变量
 
    // 对y进行相同的操作……
 
    // *****************************************************
    // 分支
    // if ( x >= y ) goto L_GR;
    // *****************************************************
    ldloc.0                     // 把x装入堆栈(value 1)
    ldloc.1                     // 把y装入堆栈(value 2)
    bge.s L_GR                 // 跳转到 L_GR 如果value1≥value2
 
    // *****************************************************
    // z = x
    // *****************************************************
    ldloc.0                     // 把第0个变量装入堆栈
    stloc.2                     // 保存到第2个变量
 
    br.s       L_CONTINUE       // 跳转至 L_CONTINUE
 
L_GR:
 
    // *****************************************************
    // z = y
    // *****************************************************
    ldloc.1             // 把第1个变量装入堆栈
    stloc.2             // 保存到第2个变量
 
L_CONTINUE:
 
    // *****************************************************
    // Console.WriteLine("{0:d}", z);
    // 注意:这一行引起装箱操作
    // *****************************************************
    ldstr      "{0:d}"  // 把字符串装入堆栈
    ldloc.2             // 把第2个变量装入堆栈 (z)
    box       [mscorlib]System.Int32   // 把Int32变为Object
    call  void [mscorlib]System.Console::WriteLine( string , object )
 
    ret
}

数组2(循环)
本程序用循环填充一个数组并打印其元素。这一次,我们增加一个静态函数ShowNumber(int), 它在main函数中调用。
命令:
  • blt.s label跳转到label 如果value 1小于 value 2. Values 1 和 2 必须在调用本命令之前装入堆栈。
  • ldelem.i4把一个数组元素装入堆栈。数组引用和下标必须在调用本命令之前装入堆栈。
  • ldarga.s argument把函数参数的地址装入堆栈。
我们可以看到,在本程序中,for 循环在MSIL中用标签来实现。
代码:
.assembly Array2 {}
/*
 
            int[] px = new int[100];
            int i;
 
            for ( i = 1; i < 100; i++ )
            {
                px[i] = i + 1;
            }
 
            ShowNumber(px[5]);
            ShowNumber(px[10]);
 
        static void ShowNumber(int n)
        {
            Console.WriteLine(n.ToString());
        }
*/
 
.method static public void main() il managed
{
    .entrypoint
    .maxstack 8
 
    .locals init ([0] int32[] px,
                 [1] int32 i)
 
    // *****************************************************
    // x = new int[100]
    // *****************************************************
    ldc.i4.s   100                      // 把常量装入堆栈
    newarr     [mscorlib]System.Int32   // 分配一个Int32型的数组
    stloc.0                             // 把它存入第0个变量
 
    // *****************************************************
    // i = 1
    // *****************************************************
   ldc.i4.1                    // 把常量装入堆栈
    stloc.1                     // 把它存入第1个变量
 
    br.s       CHECK_COUNTER    // 跳转到 CHECK_COUNTER
 
START_LOOP:
    // *****************************************************
    // px[i] = i + 1;
    // *****************************************************
    ldloc.0                     //   把第0个变量装入堆栈
                                // 堆栈: px
    ldloc.1                     // 把第1个变量装入堆栈
                                // 堆栈; px -> i
    ldloc.1                     // 把第1个变量装入堆栈
                                // 堆栈: px -> i -> i
    ldc.i4.1                    // 把常量装入堆栈
                                // 堆栈: px -> i -> i -> 1.
    add                         // 2 个值相加
                                // 堆栈: px -> i -> i+1
                                 //        (array,index,value)
    stelem.i4                   // 把值存入数组元素
                                // 堆栈[index] = value
                                // 堆栈: 空
    // *****************************************************
    // i = i + 1
    // *****************************************************
    ldloc.1                     // 把第1个变量装入堆栈
    ldc.i4.1                    // 把常量装入堆栈
    add                         // 相加
    stloc.1                     // 把值存入把第1个变量
 
CHECK_COUNTER:
    // *****************************************************
    // 如果 i < 100 跳转到循环开始的地方
    // *****************************************************
    ldloc.1                     // 把第1个变量装入堆栈
    ldc.i4.s   100              // 把常量装入堆栈
    blt.s      START_LOOP       // 如果value1<value2调转至START_LOOP
 
    // *****************************************************
    // ShowNumber(px[5]
    // *****************************************************
    ldloc.0                     // 把第0个变量装入堆栈
                                 // (array)
    ldc.i4.5                    // 把常量装入堆栈
                                // (index)
    ldelem.i4                   // 把数组元素装入堆栈
    call       void ShowNumber(int32)   // 调用 ShowNumber
 
    // *****************************************************
    // ShowNumber(px[10]
    // *****************************************************
    ldloc.0
    ldc.i4.s   10
    ldelem.i4
    call       void ShowNumber(int32)
 
    ret
}
 
.method static public void  ShowNumber(int32 n) il managed
{
 .maxstack 1
 ldarga.s   n          // 把第n个参数的地址装入堆栈
 call       instance string [mscorlib]System.Int32::ToString()
 call       void [mscorlib]System.Console::WriteLine( string )
 
 ret
}

不安全代码
本程序通过unsafe指针填充和打印一个int型数组。
在本程序中,我们将看到新的类型:int32*int32&。使用关键字pinned 可以阻止GC移动由局部指针变量指向的对象。
命令:
  • dup在堆栈上复制一个值。
  • stind.i4存储值的地址。地址和值必须在调用本命令之前装入堆栈。
Code:
.assembly Unsafe {}
/*
int[] nArray = new int[5];
int i;
int* pCurrent;
 
fixed ( int* pArray = nArray )
{
    pCurrent = pArray;
 
    for ( i = 0; i < 5; i++ )
    {
        *pCurrent++ = i + 1;
    }
}
 
for ( i = 0; i < 5; i++ )
{
    Console.WriteLine(nArray[i].ToString());
}
 
*/
 
.method static public void main() il managed
{
    .entrypoint
    .maxstack 8
 
    .locals ([0] int32[] nArray,
             [1] int32 i,
             [2] int32* pCurrent,
             [3] int32& pinned pArray)  // GC 不会移动该指针指向的对象
 
    // *****************************************************
    // nArray = new int[5];
    // *****************************************************
    ldc.i4.5                            // 把常量5装入堆栈                                        
    newarr     [mscorlib]System.Int32   // 生成数组 Int32[5]
    stloc.0                             // 存入第0个变量
 
    // *****************************************************
    // pArray = nArray    (pArray = &nArray[0])
    // *****************************************************
    ldloc.0
               // 把第0个变量装入堆栈(array)
    ldc.i4.0
               // 把常量0装入堆栈(index)
    ldelema    [mscorlib]System.Int32
               // 把array[index]装入堆栈
    stloc.3
               // 存入第3个局部变量
 
    // *****************************************************
    // pCurrent = pArray;
    // *****************************************************
    ldloc.3                     // 把第3个变量装入堆栈
    conv.i                      // 转变为int
    stloc.2                     // 存入第2个变量
 
    // *****************************************************
    // i = 0
    // *****************************************************
    ldc.i4.0                    // 把常量0装入堆栈
    stloc.1                     // 存入第1个变量
 
    // *****************************************************
    // 跳转到 CHECK_COUNTER
    // *****************************************************
    br.s       CHECK_COUNTER
 
START_LOOP:
    // *****************************************************
    // *pCurrent++ = i + 1                             
    // *****************************************************
    // 1) 保存pCurrent到堆栈,然后累加pCurrent
    ldloc.2
          // 把第2个变量装入堆栈            [pCurrent]
    dup
          // 复制栈顶的值
          //                                [pCurrent pCurrent]
    ldc.i4.4
          // 把常量4装入堆栈               [pCurrent pCurrent 4]
    add
          // 相加                           [pCurrent pCurrent + 4]
    stloc.2
          // 存入第2个变量                 [pCurrent]
         // 译注:因为int型指针是4位的,所以加pCurrent+4==*pCurrent++
 
    // 2) 把 (i+1) 保存到pCurrent
    ldloc.1
          // 把第1个变量装入堆栈           [pCurrent i]
    ldc.i4.1
          // 把常量1装入堆栈                [pCurrent i 1]
    add   // 相加                           [pCurrent i+1]
   //                                  地址    值
    stind.i4
          // 把i+1的值的地址存入pCurrent   [empty]
 
    // *****************************************************
    // i = i + 1
    // *****************************************************
    ldloc.1             // 把第1个变量装入堆栈
    ldc.i4.1            // 把常量1装入堆栈
    add                 // 相加
    stloc.1             // 存入第1个变量
 
CHECK_COUNTER:
 
    // *****************************************************
    // 如果i < 5 跳转至 START_LOOP;
    // *****************************************************
    ldloc.1                     // 把第1个变量装入堆栈
    ldc.i4.5                    // 把常量5装入堆栈
    blt.s      START_LOOP       // 如果i<5跳转至START _ LOOP
 
    // *****************************************************
    // pArray = 0               fixed 块结束
    // *****************************************************
    ldc.i4.0                    // 把常量0装入堆栈
    conv.u                      // 转变为unsigned int,并压入堆栈
    stloc.3                     // 存入第3个变量
 
    // 打印数组元素……
 
    ret
}

PInvoke
本程序使用Win32 API GetComputerName 和 MessageBox 显示计算机的名字。API的MSIL声明形式如下:
.method public hidebysig static pinvokeimpl("kernel32.dll"
                                             autochar winapi)
        int32 GetComputerName(
               class [mscorlib]System.Text.StringBuilder
                                      marshal( lptstr) buffer,
               int32& size) cil managed preservesig
{
}
 
.method public hidebysig static pinvokeimpl("User32.dll"
                                             autochar winapi)
        int32  MessageBox( native int hWnd,
                          string  marshal( lptstr) lpText,
                          string  marshal( lptstr) lpCaption,
                          int32 uType) cil managed preservesig
{
}
其调用规则与其他函数一致。


未完待续。

这篇关于MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910470

相关文章

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea

Go语言使用slices包轻松实现排序功能

《Go语言使用slices包轻松实现排序功能》在Go语言开发中,对数据进行排序是常见的需求,Go1.18版本引入的slices包提供了简洁高效的排序解决方案,支持内置类型和用户自定义类型的排序操作,本... 目录一、内置类型排序:字符串与整数的应用1. 字符串切片排序2. 整数切片排序二、检查切片排序状态:

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

QT6中绘制UI的两种方法详解与示例代码

《QT6中绘制UI的两种方法详解与示例代码》Qt6提供了两种主要的UI绘制技术:​​QML(QtMeta-ObjectLanguage)​​和​​C++Widgets​​,这两种技术各有优势,适用于不... 目录一、QML 技术详解1.1 QML 简介1.2 QML 的核心概念1.3 QML 示例:简单按钮

使用Java将实体类转换为JSON并输出到控制台的完整过程

《使用Java将实体类转换为JSON并输出到控制台的完整过程》在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用JSON格式,用Java将实体类转换为J... 在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用j

Nginx使用Keepalived部署web集群(高可用高性能负载均衡)实战案例

《Nginx使用Keepalived部署web集群(高可用高性能负载均衡)实战案例》本文介绍Nginx+Keepalived实现Web集群高可用负载均衡的部署与测试,涵盖架构设计、环境配置、健康检查、... 目录前言一、架构设计二、环境准备三、案例部署配置 前端 Keepalived配置 前端 Nginx

Python logging模块使用示例详解

《Pythonlogging模块使用示例详解》Python的logging模块是一个灵活且强大的日志记录工具,广泛应用于应用程序的调试、运行监控和问题排查,下面给大家介绍Pythonlogging模... 目录一、为什么使用 logging 模块?二、核心组件三、日志级别四、基本使用步骤五、快速配置(bas

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

Java进行日期解析与格式化的实现代码

《Java进行日期解析与格式化的实现代码》使用Java搭配ApacheCommonsLang3和Natty库,可以实现灵活高效的日期解析与格式化,本文将通过相关示例为大家讲讲具体的实践操作,需要的可以... 目录一、背景二、依赖介绍1. Apache Commons Lang32. Natty三、核心实现代

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.