最近由于需要,开始阅读 MSIL 方面的东西。我读的是《.NET 探秘——MSIL 权威指南》(《Expert .NET 2.0 IL Assembler》中译版)。感觉没什么好说的,毕竟只要对 .NET 及其后面的东西了解一些,然后当做汇编来看,就好了。剩下的就是实践。
如书上所言,前面已经有人做出了这项研究了,如 Anders Liu、装配脑袋、Flier Lu。前辈们都是老手了,我也不好说什么,毕竟我刚刚入门。
这里就贴出昨晚写的一个 HelloWorld 程序吧。读了4天的收获。为了方便没学过 MSIL 的同志们,在上面都附上了实现同样功能的 C# 代码。
嗯,高级的东西还不敢说,要再仔细看,多练习才行啊。
.assembly extern mscorlib { auto } .assembly MyIlApp { } .module MyIlApp.exe .namespace MyIlApp { // public struct VC .class public value sealed auto VC { // public int val_int32; .field public int32 val_int32 // public string val_string; .field public string val_string } // public sealed class MainClass .class public sealed auto ansi MainClass { // public static int _val; .field public static int32 _val // public static void check(int argument1) .method public static void check(int32) cil managed { // string temp; .locals init ([0]string temp) // if (MyIlApp.MainClass._val == argument1) // { // System.Console.WriteLine("The value equals to the argument."); // } // else // { // System.Console.WriteLine("The value does not equal to the argument."); // } ldsfld int32 MyIlApp.MainClass::_val ldarg.0 beq TrueEqual ldstr "The value does not equal to the argument." call void [mscorlib]System.Console::WriteLine(string) br ThisIsEnd TrueEqual: ldstr "The value equals to the argument." call void [mscorlib]System.Console::WriteLine(string) // temp = argument1.ToString(); // 注意这里用 call 而不是 callvirt,因为 argument1 是 int32 类型,未装箱的时候没有V表 ThisIsEnd: ldarga.s 0 call instance string [mscorlib]System.Int32::ToString() stloc.0 // System.Console.Write("The real value is:"); ldstr "The real value is: " call void [mscorlib]System.Console::Write(string) // System.Console.WriteLine(temp); ldloc.0 call void [mscorlib]System.Console::WriteLine(string) // return; ret } // public static void Main() .method public static void Main() cil managed { // .entrypoint 伪指令表示是程序入口点 .entrypoint /* Test Case 1 */ // string input; // int v; .locals init ([0] string input, [1] int32 v) // System.Console.WriteLine("Hi. Please input a string:"); ldstr "Hi. Please input a string:" call void [mscorlib]System.Console::WriteLine(string) // input = System.Console.ReadLine(); call string [mscorlib]System.Console::ReadLine() stloc.0 // System.Console.WriteLine(input); ldloc.0 call void [mscorlib]System.Console::WriteLine(string) // v = System.Int32.Parse(input); ldloc.0 call int32 [mscorlib]System.Int32::Parse(string) stloc.1 // MyIlApp.MainClass._val = 9000; ldc.i4 9000 stsfld int32 MyIlApp.MainClass::_val // System.Console.WriteLine(MyIlApp.MainClass._val.ToString()); ldsflda int32 MyIlApp.MainClass::_val call instance string [mscorlib]System.Int32::ToString() call void [mscorlib]System.Console::WriteLine(string) // MyIlApp.MainClass.check(v); ldloc.1 call void [MyIlApp]MyIlApp.MainClass::check(int32) /* Test Case 2 */ // VC vc1; .locals init ([2] valuetype [MyIlApp]MyIlApp.VC vc1) // vc1.val_string = "Test string of VC"; ldloca.s 2 ldstr "Test string of VC" stfld string MyIlApp.VC::val_string // vc1.val_int32 = 8; ldloca.s 2 ldc.i4.8 stfld int32 MyIlApp.VC::val_int32 // System.Console.WriteLine(vc1.val_string); ldloca.s 2 ldfld string MyIlApp.VC::val_string call void [mscorlib]System.Console::WriteLine(string) // System.Console.WriteLine(vc1.val_int32.ToString()); // 《Expert .NET 2.0 IL Assembler》上说 ldflda “不能使用值类型的实例,也不能获取指向值类型实例的对象引用或指针”,有误。为了实现类似 a.X = 100(a 为 System.Drawing.Point 类型)这样的调用,需要用到 call 指令,也就需要用到 ldflda 而不是 ldfld;不过后者可以用在不访问实例函数/字段的情况下 ldloca.s 2 ldflda int32 MyIlApp.VC::val_int32 call instance string [mscorlib]System.Int32::ToString() call void [mscorlib]System.Console::WriteLine(string) // return; ret } } }