首页
社区
课程
招聘
[zz]浅析.NET Framework对PE文件格式的扩展
发表于: 2004-8-26 12:31 10727

[zz]浅析.NET Framework对PE文件格式的扩展

2004-8-26 12:31
10727

浅析.NET Framework对PE文件格式的扩展
              WebCrazy(http://webcrazy.yeah.net)

   Microsoft .NET Framework出来小阵子了,我也自从其Beta 1以来,第一次接触。本文将从.NET生成的一个小PE文件着手,旨在理解.NET Framework对PE文件格式的扩展。这种扩展目的是让Windows系统识别Common Language Runtime(CLR)。

   PE文件是Windows系列操作系统的可执行文件格式。本文假设您对这一文件格式有相当的理解,文中未涉及PE在之前的win16及之后的win64上的讨论。在CLR出现之前,PE文件格式仅简单的由PE Header与Native Image(相对于以下介绍的CLR Header与CLR Data部分)组成。Native Image由各个section组成,如.text,.data,.rdate等等,需要指出的是PE文件的这些section名命名规则并不要求一定要以句点开头,事实上这只是Microsoft的对于代码段或数据段的默认说法,像Borland等其他编译器则相应分别命名为CODE,DATA等等。Native Image含有已编译的相应处理器的机器代码。

   在CLR出现后PE文件扩展出了另外一部分,即CLR Header与CLR Data组成的供.NET Framework运行的支撑部分。CLR Header由.NET Framework SDK的CorHdr.h中的IMAGE_COR20_HEADER结构定义。从CorHdr.h或是IMAGE_COR20_HEADER的命名中Cor的全称Com+ Runtime即可隐隐约约的看到.NET Framework的发展过程,其与COM+的渊源关系了。事实上IMAGE_COR20_HEADER在平台SDK的winnt.h中也有定义,我查阅的了随Windows XP DDK Build 2505发行的winnt.h中Microsoft在给出这个定义时的注释为COM+ 2.0 header structure,而在.NET Framework SDK中即修改为CLR 2.0 header structure了。CLR Data则包含.NET metadata, IL method bodies等等。metadata及IL method是.NET中很关键的术语。IL即Microsoft Intermediate Language的缩写。她是为了.NET跨平台、跨语言的特性而引入的,有其自身的指令集。.NET SDK中的opcode.def列出的其支持的指令集。粗粗看来这些指令集与Intel的X86指令集十分的相像,也是由Prefix指定的的双字节进行编码。

   下面的我将通过底下列出的这一段C# Console代码来简述C#编译器生成的PE文件的执行流程及PE文件的on disk结构。代只是简单的输出Hi,如下所示:

   public class App {
     static public void Main(System.String[] args) {
      System.Console.WriteLine("Hi");
     }
   }

   我们简单的使用csc /out:app.exe app.cs对其编译。生成的PE文件,与.NET出现前传统的编译器生成的PE文件一致,也含有IMAGE_DOS_HEADER,我们知道这部分的作用即是早期的DOS在遇到PE文件格式时,能判定这个可执行文件不能执行于DOS下而存在的。IMAGE_DOS_HEADER与将要谈及的一些结构在winnt.h中均有详细定义。Windows OS Loader根据IMAGE_DOS_HEADER中的e_lfanew成员定位紧挨着的IMAGE_NT_HEADERS。其定义如下:

   typedef struct _IMAGE_NT_HEADERS {
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
   } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

   我们知道IMAGE_OPTIONAL_HEADER32的成员AddressOfEntryPoint 是PE可执行文件的入口,在.NET中其仍为执行入口,这应该是很好理解的。对于一个COMIMAGE_FLAGS_ILONLY(由IMAGE_COR20_HEADER 的成员Flags 指定)的Image,如我们生成的App.exe,这个入口也即间接定位至App.exe的Import表的_CorExeMain函数。_CorExeMain对应EXE文件,由mscoree.dll导出。mscoree.dll位于%WINNT%\system32下,是Microsoft .NET Runtime Execution Engine,应该指出的她是一个Native Image,负责调用IMAGE_COR20_HEADER中的 EntryPointToken 指定的.NET Token。这才是真正IL语言的入口。

   Native Image部分的各个Section的定位,已经有很多文档介绍,而且winnt.h中都有详细的定义。我只简单的阐述一下:

   .text、.data等section定位是由IMAGE_OPTIONAL_HEADER32中的DataDirectory成员指定。DataDirectory是一个IMAGE_DATA_DIRECTORY数组,个数为MAGE_NUMBEROF_DIRECTORY_ENTRIES(当前为16)个。各个DataDirectory功能分别由IMAGE_DIRECTORY_ENTRY_***指定,如EXPORT、IMPORT等等。因为IMAGE_DATA_DIRECTORY由VirtualAddress(RVA)与Size组成,所以我们即可以很容易的找到这些Section的位置。与这些Section一样,CLR Header的定位也是DataDirectory指定,其为IMAGE_DIRECTORY_ENTRY_COMHEADER(值为14,.NET Framework SDK V1 CorHdr.h中称谓,在DDK 2505的winnt.h中为IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)。我们生成的App.exe有如下的格式:

         .
         .
         .
   AddressOfEntryPoint: 0x000022CE (+0x10)
         .
         .
         .
   DataDirectory[0] - IMAGE_DIRECTORY_ENTRY_EXPORT
     VirtualAddress: 0x00000000 (+0x60)
      Size: 0x00000000 (+0x64)
   DataDirectory[1] - IMAGE_DIRECTORY_ENTRY_IMPORT
     VirtualAddress: 0x0000227C (+0x68)
     Size: 0x0000004F (+0x6C)
         .
         .
         .
   DataDirectory[14] - IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
     VirtualAddress: 0x00002008 (+0xD0)
     Size: 0x00000048 (+0xD4)
         .
         .
         .
   OK,从DataDirectory[14]我们即可以很容易的定位CLR Header。CLR Header可以被合并到其它任何为只读属性的Section中。前面已经提及到CLR Header由IMAGE_COR20_HEADER结构定义。

   // CLR 2.0 header structure.
   typedef struct IMAGE_COR20_HEADER
   {
      // Header versioning
      ULONG cb;
      USHORT MajorRuntimeVersion;
      USHORT MinorRuntimeVersion;

      // Symbol table and startup information
      IMAGE_DATA_DIRECTORY MetaData;
      ULONG Flags;
      ULONG EntryPointToken;
      // Binding information
      IMAGE_DATA_DIRECTORY Resources;
      IMAGE_DATA_DIRECTORY StrongNameSignature;

      // Regular fixup and binding information
      IMAGE_DATA_DIRECTORY CodeManagerTable;
      IMAGE_DATA_DIRECTORY VTableFixups;
      IMAGE_DATA_DIRECTORY ExportAddressTableJumps;

      // Precompiled image info (internal use only - set to zero)
      IMAGE_DATA_DIRECTORY ManagedNativeHeader;

   } IMAGE_COR20_HEADER;
   这个结构的Flags与EntryPointToken上面已经提及。从这么多的IMAGE_DATA_DIRECTORY上看,这个定义很像IMAGE_OPTIONAL_HEADER32,后者可以理解成PE文件头的精华,其用于定位.text等Section,由Windows OS Loader执行。而前者用于定位.NET CLR Data,如MetaData、Resources、StrongNameSignature等等。不同的是IMAGE_COR20_HEADER是由mscoree.dll中的_CorExeMain(对应于EXE文件)负责调用(MSIL语言需经过JIT编译成机器码才可执行)。

   虽然EnrtyPointToken与上面的AddressOfEntryPoint均是执行入口,但却有非常大的区别。AddressOfEntryPoint是一RVA,直接指向执行地址(相对于Image Base),其只能指向一本地机器代码用于装载NET Runtime(如mscoree.dll中的_CorExeMain,对于DLL文件其可以置为0)。而EntryPointToken只是一个.NET TOKEN。TOKEN是.NET Type的唯一识别,是一个DWORD值。其最高的8bit指明何种TOKEN。其由CorHdr.h中的CorTokenType enum定义。如mdtMethodDef为0x06000000,mdtEvent为0x14000000等等,而余下的24bit则为此类TOKEN的唯一识别。EnrtyPointToken只能是一METHOD,而不能是EVENT等等。如App.Exe的EnrtyPointTokeno为0x06000001,其对应于Main Method。您可以使用ildasm.exe(随.NET Framework SDK提供)进行验证。

   App.exe的CLR Header如下(只列出了部分非空字段):

    Size: 0x00000048
    MajorRuntimeVersion: 0x0002
    MinorRuntimeVersion: 0x0000
    MetaData
    VirtualAddress: 0x0000207C
    Size: 0x00000200
    Flags: 0x00000001
    COMIMAGE_FLAGS_ILONLY
    EntryPointToken: 0x06000001

   .NET MetaData由MetaData成员指定。Microsoft在CorHdr.h中给出了ILMETHOD的on disk组织结构(IMAGE_COR_ILMETHOD)。随.NET Framework SDK也提供了一个例子metainfo用于分析Metadata。随QuickStart例子的Class Browser的ASP.NET范例也是.NET Framework很好的学习材料。Metainfo使用常规的COM方法,而Class Browser使用.NET Framework的System.Reflection Namespace。关于.NET的SOAP,Web Services,Web Forms,XML等等QuickStart真不愧为QuickStart,.NET看来是下阵子学习的方向啊。

   最后应该说明的是对于.NET我真有一种很自清新的感觉,自身也是刚刚接触,本文仅是抱着学习的态度,权当自己的学习笔记,与各位进行交流。文中有误之处或是有所建议,请联系tsu00@263.net。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (18)
雪    币: 898
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
2
菩提 应该对这方面感兴趣
2004-8-26 12:57
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
3
请问cyclotron几个问题:

1. 有没有debugger,能在IL的级别上调试程序?
2. 如何知道IL代码在文件中的对应位置?
2004-8-26 19:04
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
4
最初由 softworm 发布
请问cyclotron几个问题:

1. 有没有debugger,能在IL的级别上调试程序?
2. 如何知道IL代码在文件中的对应位置?


呵呵,我只是转载,没有调试过呢~~
不过等.NET大潮席卷之时,恐怕所有的PE工具都得重新来过,想想真是紧张啊
Softworm兄有没有调试过它的虚拟机呀,跟VB Pcode像不像?
2004-8-26 19:52
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
5
我没有crack过.NET程序。手上有个从exetools下的工具,
叫ActiveX/COM Inspector,本来带个patcher,不知道
怎么在我的W2K上patcher运行就异常。干脆自己玩玩,
结果是个.NET的。

用RemoteSoft的反编译器,效果相当好。IL还真象VB
Pcode,不过比VB PCode资料多。

可惜不知道怎么调试,用.NET的IDE加载好象不行。对.NET
程序的结构也不熟悉。不过要爆破估计不难,可以对着书,
试试NOP或者改个jmp之类的。现在我不知道怎么找文件中
的对应地址。明天再试试。:D
2004-8-26 21:42
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
6
哪些平台可以支持.Net?
我下载过一个叫Anakrino的反编译器,不过在我的Win2k sp3上一加载例子程序马上出错,不知道是不是由于操作系统的关系。
另外,哪些地方可以找到.Net程序IL的资料?
2004-8-26 22:04
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
7
留下email,明天给你发
2004-8-26 22:37
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
8
Thx~:D
cyclotron (at) citiz.net
2004-8-26 23:17
0
雪    币: 255
活跃值: (266)
能力值: ( LV12,RANK:220 )
在线值:
发帖
回帖
粉丝
9
最初由 softworm 发布
请问cyclotron几个问题:

1. 有没有debugger,能在IL的级别上调试程序?
2. 如何知道IL代码在文件中的对应位置?

1.有debug,就在.net的sdk中
2,你用MSIL 反汇编程序(Ildasm.exe)就可以
他有许多选项,要显示详细的信息需要手动设置。
另外:附件中的工具,能直接反编译成源代码
c#.....
附件:Reflector.zip
2004-8-26 23:56
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
10
cyclotron我已经发了,看看收到
没有,我的免费邮箱不大好用,又
懒得升级
2004-8-27 12:59
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
11
目前还没有,可能有延迟,收到后我会回复的:D
谢谢啦~~
2004-8-27 13:03
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
12
最初由 WiNrOOt 发布

1.有debug,就在.net的sdk中
2,你用MSIL 反汇编程序(Ildasm.exe)就可以
他有许多选项,要显示详细的信息需要手动设置。
另外:附件中的工具,能直接反编译成源代码
c#.....
附件:Reflector.zip


谢谢!

反汇编没什么问题,IDA 451可以得到IL的结果,也能
显示在文件中的offset。比ILDAsm更好。

用Remotesoft的Salamander Decompiler反编译
得到的结果也很好(C#)。

你说的debuuger,是DbgCLR.exe吗?我用这个加载exe,
不行。

另外,我试着对那个exe做patch,用ILDasm看已经改了,
但反编译到C#时,报该方法编译失败(被patch的那个)。
运行时splash window一闪就退出了。

是否.Net程序的代码有数字签名? 还是原程序有自
校验?

贴上程序。从反编译的数据看,用Base64+Rijndael,
可以写KeyGen的。我想知道为什么不能Patch,是否是
CLR什么的在作怪?

附件:CompInsp_Current_3.zip

这是原来的patch,在我的W2K SP4下不行,在WinXP Pro下可以。

附件:lcdaxci1.rar
2004-8-27 13:11
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
13
cyclotron我怀疑21cn那个破邮箱可能不行,
把那本书先贴在这里吧。

附件:part1.rar

附件:part2.rar

附件:part3.rar

附件:part4.rar
2004-8-27 13:22
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
14
程序员上有一期讲了这个东西
菩提和孙胖子应该懂一些
2004-8-27 13:56
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
15
最初由 softworm 发布
cyclotron我怀疑21cn那个破邮箱可能不行,
把那本书先贴在这里吧。


附件:part1.rar

附件:part2.rar

附件:part3.rar

附件:part4.rar


谢谢!我已经收到了,不过体积好大,估计我的小猫得拖一阵子啦……
2004-8-27 14:31
0
雪    币: 267
活跃值: (285)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
???
2004-8-28 08:47
0
雪    币: 209
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
如果没有做过代码混淆,简单程序初学者使用IDA应该可以在10分钟解决。
2004-8-28 10:09
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
18
最初由 SwordLea 发布
如果没有做过代码混淆,简单程序初学者使用IDA应该可以在10分钟解决。


老兄能否用我贴的那个软件写个教程? 爆破即可。

我的问题是这样:

patch了x1::Validate方法,刚进去的地方:

if(tmpValid)
{
}

改跳转opcode为0(NOP),细节记不清了(我这台机器没装.Net)。
用ILDasm可以看到已经改进去了。但用Salamander反编译,
报该方法编译失败。所以我觉得不大象自校验。运行时
splash window 闪一下就退出了。

用PE工具看,并没有使用.Net的strong name机制。
2004-8-28 12:05
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
19
这两天没事,又看了一下。是这样的:

patch时要考虑堆栈平衡。如:

原来的opcode为  brfalse.s 跳转,
会pop出一个int32,当然不能用nop代替。

不过,检查了别人的patch,仍然存在
这个问题:程序虽然可以运行,但
patch后,该方法反编译失败。
2004-9-8 13:16
0
游客
登录 | 注册 方可回帖
返回
//