string bytecode = "73 01 00 00 06 28 4E 00 00 0A 2A";
上面这个string干什么的?加密后的代码?某种不可思议的数字?汇编语言?
如果按照CLR IL规范来解析,我们会得到一段常见的代码。
0000 : newobj instance void WindowsApplication3.MainForm::.ctor()
0005 : call System.Void System.Windows.Forms.Application::Run()
0010 : ret
简单吗?看起来是new一个MainForm,然后call Application的Run方法,最后return。
仔细想想,这就是我们常见的,每个winform程序中缺省方式下自动生成的代码。如下:
static void Main()
{
Application.Run(new MainForm());
}
实际上等同于:
static void Main()
{
MainForm mf = new MainForm();
Application.Run(mf);
}
那么,上面那个bytecode,是如何被翻译成上述代码的?
哦,bytecode的第一个字符,就是73。有一个神奇的表格,其中一行:
73 newobj <Method> N arguments o
我们看到了,73代表一个东西叫做newobj,并且,它是一个method。我们应该能猜测出来,C#代码中应该就是一个new关键字。
第二个数字01,我们还是查找那个表格:
01 break - - -
哦,怎么是break呢?我们代码里面,不大可能new了一个对象之后,立刻就break啊?实际上,第一句newobj之后,执行的代码是从0x05,就是0x28开始(第二句) 。为什么跳跃了5个字节呢?因为IL中,并不只是包含operator,还包含了其他数据。在CLR解析过程中,它发现0x73是一个method之后,会继续取得一个所谓的metadataToken。伪代码如下:
metadataToken = ReadInt32(il, ref position);
而ReadInt32代码,如下所示:
private int ReadInt32(byte[] _il, ref int position)
{
return (((il[position++] | (il[position++] << 8)) | (il[position++] << 0x10)) | (il[position++] << 0x18));
}
为什么会这么做???
暂且不表,我们继续。通过上面的ReadInt32,position增加了4,变成了5。所以要从bytecode第0个字节跳跃5个,直奔0x28。通过查表,我们知道,0x28代表着一个call:
28 call <Method> N arguments Ret.value。通过某种手段,我们可以得到当前0x28的call的方法名,是run。
那么首先,那个神奇的表格是什么呢?就是一个二维信息,从0x00到0xFE,每个字节分别对应着某个操作,如br,call,newobj等。这个表格,可以从《Inside Microsoft.NET IL Assembler eBook》中找到。
我们查表查找到operator之后,通过一系列的Read*方法,得到metadatatoken。针对这个token,分别进行resolve,得到最后的operand。如上所述,通过Read*系列方法,我们同时移动了bytecode中的offset,可以定位到正确的下一个operator。
这段解析代码,可以到codeproject上查找SDILReader,里面的Globals.cs、ILInstruction.cs、MethodBodyReader.cs,分别对应着全局operators、IL构造、方法转换等内容。
打印 | 张贴于 2006-12-16 15:53:00 | Tag:VS.NET
留言反馈