元数据和CLR(一)

分析工具:元数据用 UtralEdit ,内存布局用 Sos 调试扩展和 vs2005 的内存,寄存器和反汇编窗口的信息。

步骤:用UE打开任意一个.net dll或者Exe文件,分析静态的元数据。

转到调试状态,结合SOS和调试器信息分析CLR的执行(2.0中的MethodTable布局和1.1变化较大,看不懂,哪有相关资料?)

通过IL和元数据可以看到.net语句的内部实现,而IL的实现只能通过反汇编信息

元数据和元数据表

一般为描述其他对象而建立的对象都会加上“元”,元数据就是描述其他数据的数据,在.net中,其他数据指.net对象,引用到的对象及他们的关系。

元-元数据是描述元数据的数据,他们描述了元数据的构成,有了他们我们才能定位元数据表中每一条记录。

元数据的逻辑结构类似于数据库中的表,所以被称为元数据表。元数据表有列和行,每一行都是不重复的,都有唯一的RID(表索引),RID就像是数据库表的主建。数据库有Schema,如每一列的类型,大小等,元数据也有类似的东西,他们被称为“元-元数据”,包含表中记录的大小,列的大小,偏移等。

PE中比较固定的部分(32位系统)

IMAGE_DOS_HEADER 占40h byte 从00h---39h。其中从0x3c开始的最后四个字节为e_lfanew,为指向pe signature的文件指针。pe signature为00 00 45 50,占四个字节,e_lfanew和pe signature之间是实模式残余程,从40h-79h。80-83为四字节pe signature

后面是IMAGE_FILE_HEADER(coff header)从84h-97h,占14h

Peheader从98h-178h占E0h(224 bytes)

Pe header中:从98h开始偏移96字节(32位pe头)是_IMAGE_DATA_DIRECTORY 。共16个,每个表占八字节。

之后是区域头,每个头占28h(40),共三个(个数是有Coff头的Numberofsections定义):从178h-1a0h是.text

1a0-1c8 是 rsrc

1c8-1f0 是.rsloc

 

文件指针和RVA

在文件被加载到内存之前,项在文件中的偏移量,Rva,Va是加载到内存之后的相对地址(偏移量)和地址。

RID和Token

RID是元数据表的行索引,只能在元数据表之间引用,如TypeDef表中通过RID定位此类型包含的第一个字段在Filed表中的RID。

通过列类型代码(元-元数据有定义)可以确定列所引用的表(隐式),因此RID也是可以定位表的,只是在元数据外不能被访问。

Token是表索引加上RID,他们显式的确定了是哪个表中的RID,因此可以被外部引用。在IL中变量,常量等都是通过token引用的,他们会被编译为Token。有token类型的表共有24个,另外20个表没有token他们不能被外部引用到,只能在元数据间引用。

其中有个特别的token type 0x70000000,他的Token的RID部分并不是真正的RID,因为他们存在于#US流中,此流包含用户定义的数据,没有任何元数据信息,元数据也不会引用到#Us。他的RID部分是用户字符串在#US中的偏移量,通过他可以定位用户定义的字符串。而普通的Token,却要先定位RID,再根据元数据表的列类型等信息定位元数据信息。

流和堆

此处的堆和数据结构中的堆没有关系,是两个概念。分为三种String,GUID,blob

元数据提供了六种命名堆:

#String:元数据引用到的内容,如类名,方法名,变量名等

#US:用户定义的blob堆(不是String堆),包含字符串常量等,可以被ldstr指令直接寻址。不能被元数据直接引用,但可以被IL以及外部api引用。如string s =”dd”; dd存储在#us,s存储在#string

#Blob:元数据引用的二进制对象,不能包含用户定义的对象。

#GUID:各种唯一标志如Modle元数据表的Mvid等。

#~和#-(镜像文件中只能包含一个):元数据流,包含元数据头,元数据表等,最复杂的堆。他会引用到(#String,#blob,#GUID流)

Flag和Signature

Flag包含可见性,布局(Layout),类型语义,实现,字符串格式化等Flag,通过Flag我们可以确认这几类信息。

Signature:(blob流中)

定位Token

以用户定义的字符串为例子(#US):

用户字符串定义在#US流,计算方法是:流的首地址(即元数据头地址,因为流头在元数据头中)+#US流的偏移量(offset然后)然后加上字符串的偏移(token的RID部分)就是字符串在#US流中的偏移。

定位元数据表

首先要找到元数据头(metaDataHeader),方法和Cor2.0Header一样,只是Rva换成Cor2.0Header中MetaDataDir表的Rva。元数据头由STORAGESIGNATURE、STORAGEHEADE、流头构成。后面为STORAGESTREAM,依次包含,#String,  #Blob, #Guid,  #US(User String)和#~流

元数据表被存储在#~流中,找到元数据表头,加上偏移量和元-元数据信息就可以定位。具体参见定位Cor2.0Header。

定位区域头(Section Header)

区域头共有三个,每个头占28h(40)(头个数是有Coff头的Numberofsections定义):从178h-1a0h是.text

1a0-1c8 是 rsrc

1c8-1f0 是.rsloc

定位PE头(数据目录表)

数据目录表位于PE头,从98h开始偏移96字节(32位pe头)是_IMAGE_DATA_DIRECTORY 。共16个,每个表占八字节。

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

 

数据目录表指向的数据在上面的三个Section中,可以根据 _IMAGE_DATA_DIRECTORY的VirtualAddress(rva)判断,其落在那个Section中:VirtualAddress<rva< VirtualAddress+ SizeOfRawData。

十六个表中和CLI关系最密切的是第十五个表Cli header,通过上面的方法可以算出他在哪个区中。

 

定位Cor2.0Header

知道为于哪个Section,就可以定位Cor2.0Header了,根据计算可知,Section是.text,地址每次编译都会有区别。根据CliHeader的Rva和此section的virtualAddress 、PointerToRawData  可以计算出COR20Header的地址:(rva-virtualAddress) +  PointerToRawData 

Cor头含有七个表,最后一个为保留表(2.0只发现了前六个,可那2.0仍然没有用到此表),其中重要的是MetaDataDir表,地址可以根据同样方法计算得到。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值