原文:
微软.NET通用语言运行库概观
by Benny/
翻译:tankaiha
整个世界好像都在谈论微软的被称作.NET的新平台。2002年1月的推出的.NET FrameWork SDK版本包含了许多基本服务。并且,用在.NET下可视化开发的Microsoft Visual Studio.NET测试版也出现在网上,你可以用它创建自已的程序。我写这篇文章是因为我不能坐等别人先于我研究这个平台,你是知道的,我是个充满好奇心的人。
MS.NET有什么新玩意儿?
......................
Microsoft .NET的体系是基于组件和平台独立的,完整给出它的定义需要太多的文字,所以我在这里尽量缩短篇幅,只谈及一些重要的内容。.NET FrameWork SDK提供了在非.NET机器上运行的必需部分。完整的.NET操作系统将随WinXP的后继版本发行。但是微软会想方设法推广他们的新平台,就像当初的Win32一样——发行Win32s,适用于Win16位系统的Win32限制版。今天,他们推出了.NET FrameWork SDK,它和.NET体系的关系就像Win32s之于完整的Win32。
本篇文章要讨论的核心内容就是.NET通用语言运行环境(.NET Common Language Runtime environment)。
回顾历史
...........................
在过去,创建组件真他妈麻烦!如果你的组件是用C++写的,那VB就不能调用,反之亦然——这是因为一种语言写的程序尽然需要另一咱语言写的运行时来支持。(原句:that's becoz application written in one specific language used another runtime environment than the application written in another language)微软使用了很多方法来解决这个问题:DDE,OLE,COM(1.0,DCOM,COM+等等)让我们看一下COM——在过去,使用COM显得非常有效,所以也很成功,所以它被高级语言编程者广泛使用。但有个小问题:在C++中调用COM非常麻烦(至今仍是这样)。在VB中虽然比较方便,但难以发挥COM的全部功能,而让不同语言编写的程序进行合作又总是存在这样那样的问题。现在也还是这样。
“全新”的架构
...........................
微软又一次偷窃了别人的成果,并用来提高自已的技术。MS.NET就是例子。.NET环境中只存在一种代码,就是微软自已的中间语言(元代码、伪代码以及类似JAVA的类),还有一个运行时环境(CLR)和库(Frameworks)。
微软的通用语言运行时
...........................
通用的模型提供了所有的服务,你可以使用任何一种语言来访问它们。当然,你也可以编写自已的服务供所有的.NET语言使用。因为,无论你使用何种编程语言,最后都将被编译成与语言无关的元代码。程序都是在运行的时候被CLR即时编译成可执行代码的,可以看出,.NET整体上看就是JAVA,没什么新东西。
本机语言
...........................
我不是要吓唬谁:C++和VB都可以在.NET中使用。甚至JAVA也可以被当作.NET本机语言。微软还设计了两种全新的语言:C#和J#(读作C Sharp、J Sharp)。
1)C#是一种全新的且非常高效的语言,用来编写.NET应用程序。当初我刚开始学习它时,充满了兴趣。微软设计了一种超级语言:它和C++很像,但又包含了很多别的语言的优点(VB,JAVA等)。我不得不承认,即使不算上代码良好的可读性这条优点(因为许多别的语言也具有这个优点,比如C++、JAVA、PERL),微软设计的C#是世界上最好的语言之一。真的!
2)J#是为JAVA程序员设计的。它和JAVA很像,JAVA程序员可以很容易地将代码移植到.NET上来。还有一些著名的语言(比如PERL),不久也会被移植到.NET的。
装配件
...........................
嗯,装配件对于程序员来说是个新名词。好像微软的本能就是挑战一切现有标准,还记得吗:Program->Application, Directory->Folder, Link->Shortcut等等等等。但是,装配件不是普通的可执行文件——它是一个完整的程序体(原文:application comple)。装配件包含了程序需要的所有信息。你可以将它想像为一个可执行文件,且包含了所有的元代码、数据和其它运行时必需的信息。每个装配件都有其包含的所有部件的列表,这被称作MANIFEST。
微软中间语言
...........................
我已经简短地概述了.NET CLR的相关知识,下面给你演示一些元代码(又叫MSIL)。
这是我编写的很简单的程序(simple.cs)
<------------------ cut ------------------>
using System;
class Sample {
public static void
Console.WriteLine("Hello world");
}
}
<------------------ cut ------------------>
通过命令行“CSC simple.cs”来编译它,随后生成了新的文件simple.exe。用相关工具打开,你会发现它和普通的PE文件不同:它不含可执行代码,还有一些很奇怪的结构。是的,它是MSIL PE文件。
用ILDASM.EXE将它反汇编成IL代码,我们可以看到如下的树状结构。
<------------------ cut ------------------>
___[MOD] D:\!!work\.net\sample.exe
| M A N I F E S T
|___[CLS] Sample
| | .class private auto ansi beforefieldinit
| |___[MET] method .ctor : void()
| |___[STM] method
|
<------------------ cut ------------------>
除去所有不必要的信息(注释、资源、垃圾代码...),你就得到了simple.il
<------------------ cut ------------------>
assembly extern mscorlib
{
.publickeytoken = (B7
.ver 1:0:2411:0
}
.assembly sample
{
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module sample.exe
.imagebase 0x00400000
.subsystem 0x00000003
.file alignment 512
.corflags 0x00000001
.class private auto ansi beforefieldinit Sample
extends [mscorlib]System.Object
{
.method public hidebysig static
void
{
.entrypoint
// Method begins at RVA 0x2050
// Code size 11 (0xb)
.maxstack 8
IL_0000: /* 72 | (70)000001 */ ldstr "Hello world"
IL_0005: /* 28 | (
(string)
IL_
}
}
<------------------ cut ------------------>
看到了没?这些就是元代码。它看上去很容易理解,特别是对于ASM程序员,甚至不需要任何注释。也许有人会问“mscorlib”是什么?事实上它是存储在你机器上某处的一个DLL文件,里面包含了所有的类库。
现在,删除所有MS编译器添加的代码,再利用ILASM.EXE将它编译成可执行文件:ILASM sample.il
我们得到了2KB大小的“Hello World”可执行文件。现在做什么?让我们来看看它的结构,它和普通的PE文件有什么不同。
<------------------ cut ------------------>
File type: WINDOWS EXECUTABLE
File Header:
Machine: 0x
Number of Sections: 2
Time Date Stamp: 0x
Pointer to Symbol Table: 0x00000000
Number of Symbols: 0
Size of Optional Header: 0x00E0 (224)
Characteristics: 0x010E
File is executable.
Line numbers stripped from file.
Local symbols stripped from file.
32 bit word machine.
Optional Header:
Magic: 0x010B
Linker Version: 6.00
Size of Code: 0x00000400 (1024)
Size of Initialized Data: 0x00000200 (512)
Size of Uninitialized Data: 0x00000000 (0)
Adress of Entry Point: 0x0000229E
Base of Code: 0x00002000
Base of Data: 0x00004000
Image Base: 0x00400000
Section Align: 0x00002000
File Align: 0x00000200
Operating System Version: 4.00
Image Version: 0.00
Subsystem Version: 4.00
Size of Image: 0x00006000 (24576)
Size of Headers: 0x00000200 (512)
Checksum: 0x00000000
Subsystem: 0x0003 (Windows character subsytem)
DLL Characteristics: 0x0000
Size of Stack Reserve: 0x00100000 (1048576)
Size of Stack Commit: 0x00001000 (4096)
Size of Heap Reserve: 0x00100000 (1048576)
Size of Heap Commit: 0x00001000 (4096)
Loader Flags: 0x00000000
Number of Rva and Sizes: 16
<------------------ cut ------------------>
文件头正常,我们继续
<------------------ cut ------------------>
Data Directory:
Export directory: VA: 0 Size: 0
Import directory: VA: 0x00002250 Size: 0x0000004B (75)
Resource directory: VA: 0 Size: 0
Exception directory: VA: 0 Size: 0
Security directory: VA: 0 Size: 0
Base relocation table: VA: 0x00004000 Size: 0x
Debug directory: VA: 0 Size: 0
Architecture-specific data: VA: 0 Size: 0
RVA of global pointer: VA: 0 Size: 0
Thread local storage directory: VA: 0 Size: 0
Load configuration directory: VA: 0 Size: 0
Bound import directory: VA: 0 Size: 0
Import address table: VA: 0x00002000 Size: 0x00000008 (8)
Delay load import descriptors: VA: 0 Size: 0
COM run-time descriptor: VA: 0x00002008 Size: 0x00000048 (72)
(unknown directory entry): VA: 0 Size: 0
Import Table:
mscoree.dll
Import Adress Table: 0x00002000
Import Name Table: 0x00002278
Time Date Stamp: 0x00000000
Index of first forwarder reference: 0x00000000
0x00002280 0 _CorExeMain
Section Table:
Section Header #1
Name: .text
Virtual Size: 0x
Virtual Address: 0x00002000
Size of Raw Data: 0x00000400 (1024)
File Pointer to Raw Data: 0x00000200
File Pointer to Relocation Table: 0x00000000
File Pointer to Line Numbers: 0x00000000
Number of Relocations: 0
Number of Line Numbers: 0
Characteristics: 0x60000020
Section contains code.
Section is executable.
Section is readable.
Section Header #2
Name: .reloc
Virtual Size: 0x
Virtual Address: 0x00004000
Size of Raw Data: 0x00000200 (512)
File Pointer to Raw Data: 0x00000600
File Pointer to Relocation Table: 0x00000000
File Pointer to Line Numbers: 0x00000000
Number of Relocations: 0
Number of Line Numbers: 0
Characteristics: 0x42000040
Section contains initialized data.
Section can be discarded.
Section is readable.
<------------------ cut ------------------>
就是这儿!大体上看去,.NET可执行文件只包含一个必须的段“.text”。程序只导入了一个叫作
“_CorExeMain”的函数,且程序入口是40229Eh。
:0040229E FF2500204000 jmp [SAMPLE.00402000]
这句就是跳转到MSCOREE.DLL中的代码。我们可以把它看作.NET运行可执行文件中的元代码的调度员,每个程序开始都是这样调用。.text段的开始8个字节是_CorExeMain的API虚拟地址(virtual address),随后的72个字节是CLR头。第一个字节是CLR头的大小:72。后面是一些其它的数据(flagz, version of compiler, entrypoint token),再后面是数据目录,和PE文件头一样。其中有效的数据目录包括:元数据目录、资源目录、强名称目录、CodeManager表、VTableFixups目录、导出地址目录和预编译头。目录中还包含了入口的相对虚拟地址RVA和它的大小。通常,元数据从第80个字节开始(402050h)——其中的第一条数据402010h就是元数据和它的地址。(译者:建议结合.NET下PE文件结构看)
(下面就是我个人的猜测了。我不敢确定,只是凭经验断点,如果你知道,请一定告诉我!)
看来第一条数据(402050h)是关于方法/模块的记录,后面跟着元数据,因为我发现元数据总是紧跟着元代码存储。
我希望有一天网络上会出现一些关于MSIL和CLR内部结构的文档,让我们研究得更深入一些。(译者:现在已经有了Inside Microsoft .NET IL Assembler,网上还有一些不错的文章。)
最后
...........................
好了,我知道的暂时就这么多。我们能利用这些已掌握的信息做些什么呢?微软不会改变代码的形式,所以我们能进行更深入的研究。并且,借助MSDN和一些.NET工具的帮助,我们能够编写出覆盖式的MSIL PE病毒,以上述内容为基础。如果有人搞出了CLR头的结构,我确定很快便会有第一个.NET附着式的病毒出现——既然有JAVA病毒,.NET CLR为什么不行呢?
大家有福了,你可以在
好了,就这么多了。如果你了解我所不知道的信息,希望多多指教!谢谢!
....................................................
. Benny /
. benny@post.cz
. http://benny
... perfectionist, maximalist, idealist, dreamer ...
译者后记:翻译文章太累了,不过翻译完经典的文章后总有一些成就感!.NET的知识我也刚学不久,也许连入门都谈不上,所以尽管是2年前的老文,翻译中仍难免出现BUG,大家不要向我扔鸡蛋就行了。让我们共同在.NET的世界中享受virus coding的乐趣吧!
tankaiha
2005.2.4
浙公网安备 33010602011771号