首页 > 编程知识 正文

geneva,cin的头文件

时间:2023-05-06 08:23:49 阅读:163771 作者:4602

目录预备知识一、相关实验二、章节概述三、Python的struct模块1.struct.pack(fmt,v1,v2,)2. struct.unpack (fmt,string )3.

知识一、相关实验

本实验要求认真学习并完成《IMAGE_DOS_HEADER解析》、《PE头之IMAGE_FILE_HEADER解析》、《PE头之IMAGE_OPTIONAL_HEADER解析》、0103010。

二、部分概述PE文件的DOS标题和PE文件的标题部分之后的数据与部分表是多个不同的部分,部分表包含多个部分标题,每个部分标题对应于PE文件的一个部分

PE文件通常至少包含两个部分。 一个是数据块,另一个是代码块。 每个部分都有一个用于区分部分的名称。 例如,数据块的名称通常为. data,代码块的名称通常为. text。 使用节区名称只是为了方便人们区分不同的节区,实际上节区名称可以自由修改。 因为这对操作系统来说无关紧要。

在PEiD上查看PE文件的部分时,通常看起来如下所示。

三、Python的struct模块使用Python的struct模块将数据序列转换为指定类型的数据,可以指定是大字节序还是小字节序。 struct模块还支持将指定类型的数据转换为数据序列。

结构模块中最常用的两个函数是pack和unpack。

1.struct.pack(fmt,v1,v2,……)将v1、v2等数据按照fmt指定的格式转换为数据序列,函数返回字符串序列。

2.struct.unpack(fmt,string ),以fmt指定的格式分割数据序列string。 是pack函数的逆操作,函数返回一个列表(即使只返回一个数据也返回一个列表)。

3.fmt支持的部分格式如下图所示

fmt的第一个字符用于指定是大字节序还是小字节序。 小字节序使用字符“”,大字节序使用字符“”。

例如,如果需要将x4Dx5A转换为小字节序的WORD类型,即unsigned short数据,请执行struct.unpack(h ),“x4Dx5A”)0)

实验目的1 )学习PE文件的章节标题及章节知识;

2 )掌握RVA和文件偏移地址的转换算法

3 )编程实现RVA和文件偏移地址的转换。

实验环境

服务器: Windows XP,IP地址:随机分配

辅助工具: C32Asm、PEiD、Python

实验步骤节表头以及节区分析。

前面的实验已经介绍了PE文件开头的IMAGE_DOS_HEADER、DosStub以及IMAGE_NT_HEADERS,紧跟在IMAGE_NT_HEADERS之后的是节标题,即IMAGE_NT_HEADERS

每个节都有一个节标题,节标题和节数由image _ nt _ headers.file header.number of sections字段中的值决定。 与节标题相对应的结构为IMAGE_SECTION_HEADER,在WinNT.h头文件中定义如下:

typedef struct _ image _ section _ header { byte name [ image _ sizeof _ short _ name ]; union { DWORD PhysicalAddress; DWORD VirtualSize; (} Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER; 各字段的说明如下。

1.Name,相应部分的名称。 其中IMAGE_SIZEOF_SHORT_NAME的值固定为8。 如果名称长度小于8,则以空字符结束。 如果名称长度为8,则数组长度为8,因此没有空字符。 节的名称通常以. text、 data等点开头。 通常,可以自由修改此名称。

2.VirtualSize,如果未对齐,则块中所有数据的大小。

3.VirtualAddress,将块加载到内存中时的RVA,该值始终是SectionAlignment的整数倍。

4.SizeOfRawData,块数据在磁盘文件中通过文件对齐的大小。

5.PointerToRaw

Data,区块数据在磁盘文件中的偏移地址。
6.PointerToRelocations、PointerToLinenumbers、NumberOfRelocations、NumberOfLinenumbers:不是很重要的字段,这里不作研究。
7.Characteristics,区块的属性值,表明区块的可读、可写、可执行等相关属性。
现在我们使用C32Asm来分析节表头中的一个元素,操作过程如下:
1.使用C32Asm打开C:PEnotepad.exe文件;
2.根据IMAGE_DOS_HEADER的e_lfanew字段,可以知道PE头的文件偏移地址为0xE0;
3.根据IMAGE_FILE_HEADER的SizeOfOptionalHeader字段,可以知道可选头的大小为0xE0,因为Signature占用4字节,FileHeader占用20字节,所以IMAGE_NT_HEADERS的大小为:0xE0+4+20=248字节;
4.PE头的偏移为0xE0,大小为248字节,因此可以计算出节表头所在的文件偏移值为0xE0+248=472,即十六进制的0x1D8;
5.IMAGE_SECTION_HEADER结构体占用40字节,因此第一个节表头的数据范围是从0x1D8开始的40字节数据,如下图所示:

将这些数据内容对应到IMAGE_SECTION_HEADER各个字段的值,如下图所示(删除了其中不重要的字段PointerToRelocations等):

这与在PEiD中看到的解析结果是一致的:使用PEiD打开C:PEnotepad.exe,然后查看节区数据,看到的第一个区块.text与我们分析的结果是一致的,如下图所示:

实验步骤二

RVA与文件偏移地址的转换。
节区的大小是需要进行对齐处理的,而且在文件中的对齐值与在内存中的对齐值是不一样的,在IMAGE_NT_HEADERS的OptionalHeader中指明了这两个值(FileAlignment和SectionAlignment)。节区的起始地址总是进行对齐后的值,下面以文件偏移值为例进行讲解。
首先使用PEiD打开C:PEnotepad.exe文件,点击主界面中“子系统”右侧的“>”按钮来查看更详细的信息,从中可以看到PE文件节区的数目为3,文件对齐值为0x200,节区(内存)对齐值为0x1000,如下图所示:

在实验步骤一中,我们已经计算出了节表头的文件偏移值为0x1D8,而且知道每个节表头的大小为40字节,那么3个节表头的大小就是120字节,因此可以计算出节表头结束的偏移值为:0x1D8+40*3=0x250。
在PEiD中可以看到区块的文件对齐值为0x200,从文件偏移的0x250开始第一个对齐的文件偏移地址是0x400(如果n%m等于0,则认为n是关于m对齐的)。在实验步骤一中,我们已经计算出了第一个区块.text的PointerToRawData值为0x400,与我们这里得到的对齐值是一样的。
那么,在文件数据偏移0x250至0x400之间就存在一个间隙,这个间隙通常使用0x00进行填充。同样的,节区与节区之间也会存在因为对齐而导致的间隙。
可以想象,当PE文件被映射到内存时,也会存在同样的间隙,只不过间隙的大小是不一样的,因为FileAlignment和SectionAlignment的大小不一样,如下图所示:

正是因为FileAlignment和SectionAlignment的不一致,造成了PE文件数据在磁盘上的分布与在内存中的分布存在差异,最终导致了文件偏移值和相对虚拟地址(RVA)之间的差异。从图中可以看到PE文件在磁盘文件中和在内存映像中的前面一部分是一样的,即对于在DOS头、PE文件头、节表头中的数据而言,RVA与文件偏移值是一样的,在区块中的数据由于FileAlignment和SectionAlignment不一样,其RVA与文件偏移地址是不一样的,但是两者之间可以进行相互转换。
节区中RVA值转换为Offset(文件偏移)的步骤如下:
1.通过节表头数组可以遍历所有节区的信息,包括节区的起始RVA地址、起始的文件偏移地址、以及节区的大小;
2.判断给定的RVA值落在哪个节区上,对应的,可以计算出这个RVA与节区起始RVA的差值diff;
3.查看包含待转换的RVA的节区的文件偏移起始地址,加上差值diff便是转换得到的文件偏移地址。
对于C:PEnotepad.exe而言,RVA值0x9100转换为文件偏移值是多少呢?下面通过实验来讲解RVA转换为文件偏移地址的过程:
1.使用PEiD打开C:PEnotepad.exe,查看各个节区的相关参数,如下图所示:

2.从图中可以看出,0x9100这个RVA位于.data节区,且该节区的起始RVA为0x9000,因此计算出差值为0x9100-0x9000=0x100;
3…data节区的文件偏移地址为0x7C00,加上上面计算出来的差值,结果为0x7C00+0x100=0x7D00,这个就是转换得到的文件偏移地址。

实验步骤三

编程实现RVA到文件偏移值的转换。
要实现RVA到文件偏移值的转换,最主要的工作就是找到节表头的位置并对其进行遍历,之后进行一些转换工作即可,这里我们将使用Python实现这个转换工作。
几个关键步骤的描述:
1.如何找到IMAGE_NT_HEADERS?因为IMAGE_DOS_HEADER的大小固定为0x40,而e_lfanew为DosHeader的最后一个DWORD类型的数据,所以e_lfanew的偏移值为0x3C,根据e_lfanew的值可以找到NtHeaders的位置;
2.如何找到区块的数量?NumberOfSections位于FileHeader中偏移0x02的位置,而FileHeader位于NtHeaders偏移0x04的位置,所以NumberOfSections相对于NtHeaders偏移6;
3.如何找到节表头的位置?根据FileHeader的SizeOfOptionalHeader字段可以确定OptionalHeader的大小,进而确定整个NtHeaders的大小,FileHeader在NtHeaders中的偏移值为20;
4.通常而言,第一个区块的起始文件偏移地址为0x400,所以只需要读取头部0x400大小的数据即可;
5.通过DosHeader的e_magic以及NtHeaders的Signature字段,可以判断PE文件的合法性。
进行RVA到文件偏移地址转换的Python脚本的代码如下(实验机器上的C:PERvaToOffset.py):

import sysimport structdef read_pe_header(pe_file_path): # 读取PE文件头部的0x400字节的数据 f = open(pe_file_path, "rb") data = f.read(0x400) f.close() return datadef UnpackDWORD(data): # 将数据转换为小端模式下的DWORD类型的值 return struct.unpack("<L", data)[0]def UnpackWORD(data): # 将数据转换为小端模式下的WORD类型的值 return struct.unpack("<H", data)[0]def RvaToOffset(pe_file_path, rva): """将rva转换为指定PE文件的Offset地址""" # 读取文件头部数据 data = read_pe_header(pe_file_path) # 提取出IMAGE_DOS_HEADER结构的数据 dos_header = data[:0x40] # 计算IMAGE_DOS_HEADER中e_lfanew字段的值 e_lfanew = UnpackDWORD(dos_header[0x3C:]) # 计算可选头IMAGE_OPTIONAL_HEADER结构的大小 optional_header_size = UnpackWORD(data[e_lfanew+20:e_lfanew+22]) # 计算IMAGE_NT_HEADERS结构的大小 pe_header_size = 4 + 20 + optional_header_size # 计算PE文件节区的数量,即IMAGE_FILE_HEADER的NumberOfSections字段 section_num = UnpackWORD(data[e_lfanew+6: e_lfanew+8]) # 计算节表头的文件偏移位置 offset = e_lfanew + pe_header_size # 一个节表头的大小,即IMAGE_SECTION_HEADER的大小 section_header_size = 40 # 如果在节表头之前的数据内,不需要转换,rva就是文件偏移地址 if rva <= offset + section_num*section_header_size: return rva # 遍历节表头 for i in xrange(0, section_num): # 提取一个节表头的数据 section = data[offset:offset+section_header_size] # 节表头对应的节区的大小 section_size = UnpackDWORD(section[8:12]) # 节表头对应的节区的起始RVA section_rva = UnpackDWORD(section[12:16]) # 节表头对应的节区的起始文件偏移值 section_offset = UnpackDWORD(section[20:24]) # 如果待转换的rva落在这个节区上 if rva in range(section_rva, section_rva + section_size): # 计算并返回得到的文件偏移地址 return rva - section_rva + section_offset # 遍历下一个节区 offset = offset + section_header_size # 如果rva既没有在文件头中,也没有出现在节区中 # 说明rva在区块空隙中,无法转换为文件偏移值 return -1if __name__ == "__main__": # main部分 if len(sys.argv) == 3: # 通过命令行参数获得PE文件的路径 pe_file_path = sys.argv[1] # 通过命令行参数获得RVA值,注意是16进制形式 rva = int(sys.argv[2], 16) # 调用函数RvaToOffset进行计算 offset = RvaToOffset(pe_file_path, rva) # 打印计算的结果 print "RVA: 0x%08X -> Offset: 0x%08X" % (rva, offset)

使用RvaToOffset.py时需要通过命令行参数传入PE文件的路径以及16进制格式的RVA值(0xXX形式),如果RVA转换失败,将返回-1。下面我们使用C:PEnotepad.exe进行测试:
1.通过“开始菜单”、“运行”来打开cmd,使用cd C:PE进行目录切换;
2.选取notepad.exe的RVA值0x0000B000,这个RVA是.rsrc区块的起始RVA地址;
3.在命令行下输入“RvaToOffset.py notepad.exe 0xb000”来进行转换。
转换得到的结果为0x00008400,如下图所示:

这与在PEiD中看到的notepad.exe的.rsrc节区的起始文件偏移地址是一致的,如下图所示:

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。