手工打造PE文件:[4]详解块表
操作方法
- 01
块表: 接下来是各段头部,我们这里有3个段,.text(代码段), .rdata(只读数据段),data(全局变量数据段)。每段是一个IMAGE_SECTION_HEADER 结构,具有10个成员。首先我们来看.text段。 typedef struct _IMAGE_SECTION_HEADER { 0x00 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { 0x08 DWORD PhysicalAddress; 0x08 DWORD VirtualSize; } Misc; 0x0c DWORD VirtualAddress; ;区块的RVA地址 0x10 DWORD SizeOfRawData; ;在文件中对齐后的尺寸 0x14 DWORD PointerToRawData; ;在文件中偏移 0x18 DWORD PointerToRelocations; ;在OBJ文件中使用,重定位的偏移 0x1c DWORD PointerToLinenumbers; ;行号表的便宜(供调试用) 0x20 WORD NumberOfRelocations; ;在OBJ文件中使用,重定位项数目 0x22 WORD NumberOfLinenumbers; ;行号表中行号的数目 0x24 DWORD Characteristics; ;区块的属性 }; 成员1,8个字节,表识该段的名称,我们这里是.text,那么此值是他的ASCII码应该为“2E74657874000000”。 成员2,4个字节,表示有效代码所占的字节数。我们这里所有代码数一下总共26h个,固此值为“26000000”。 成员3,4个字节,表示在.text段映射到内存中的起始地址,那么这个值如何得来呢?我们知道.text是紧跟PE结构后的,然后整个PE结构映射到内存后占的大小为1000h(因为PE结构小于1000h个字节,而对齐力度粒度是1000h),那么此值便得到了,为“00100000”。 成员4,4个字节,表示.text段在文件中所占的大小。因为我们的实际代码只有26h个字节,那么这个值是不是26h呢?并不是,一定要注意段在文件中的对齐粒度是200h,所以此值为“00020000”。 成员5,4个字节,表示.text段在文件中的起始地址,上面已经计算过PE文件的总长度为400h,他实际上也就是.text的起始偏移地址,此值为“00040000”。 成员6,7,8,9,均占2个字节,都仅用于目标文件,我们这里统统填为零。 成员10,4个字节。包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。这个值实际上是二进制位进行或运算得到的值。各二进制位表示的意义如下: bit 5 (IMAGE_SCN_CNT_CODE),置1,节内包含可执行代码。 bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA)置1,节内包含的数据在执行前是确定的。 bit 7 (IMAGE_SCN_CNT_UNINITIALIZED_DATA) 置1,本节包含未初始化的数据,执行前即将被初始化为0。一般是BSS. bit 9 (IMAGE_SCN_LNK_INFO) 置1,节内不包含映象数据除了注释,描述或者其他文档外,是一个目标文件的一部分,可能是针对链接器的信息。比如哪个库被需要。 bit 11 (IMAGE_SCN_LNK_REMOVE) 置1,在可执行文件链接后,作为文件一部分的数据被清除。 bit 12 (IMAGE_SCN_LNK_COMDAT) 置1,节包含公共块数据,是某个顺序的打包的函数。 bit 15 (IMAGE_SCN_MEM_FARDATA) 置1,不确定。 bit 17 (IMAGE_SCN_MEM_PURGEABLE) 置1,节的数据是可清除的。 bit 18 (IMAGE_SCN_MEM_LOCKED) 置1,节不可以在内存内移动。 bit 19 (IMAGE_SCN_MEM_PRELOAD)置1, 节必须在执行开始前调入。 Bits 20 to 23指定对齐。一般是库文件的对象对齐。 bit 24 (IMAGE_SCN_LNK_NRELOC_OVFL) 置1, 节包含扩展的重定位。 bit 25 (IMAGE_SCN_MEM_DISCARDABLE) 置1,进程开始后节的数据不再需要。 bit 26 (IMAGE_SCN_MEM_NOT_CACHED) 置1,节的 数据不得缓存。 bit 27 (IMAGE_SCN_MEM_NOT_PAGED) 置1,节的 数据不得交换出去。 bit 28 (IMAGE_SCN_MEM_SHARED) 置1,节的数据在所有映象例程内共享,如DLL的初始化数据。 bit 29 (IMAGE_SCN_MEM_EXECUTE) 置1,进程得到“执行”访问节内存。 bit 30 (IMAGE_SCN_MEM_READ) 置1,进程得到“读出”访问节内存。 bit 31 (IMAGE_SCN_MEM_WRITE)置1,进程得到“写入”访问节内存。 在我们这里,因为这是代码段,所以bit 5 (IMAGE_SCN_CNT_CODE)位置1,一般代码段都含有初始化数据,那么bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA)位置1,有因为代码段的代码可以执行的,所以bit 29 (IMAGE_SCN_MEM_EXECUTE) 置1,那么这3个二进制位进行或运算最终得到此成员值“20000060”。 这个整个.text头就编写完毕,按照上面的方法,分别在编写.rdata段和.data段。因为要对齐,所以后面的代码用零补齐。 至此,我们基本上已经完成了PE结构的编写,但是,此时的程序还没达到效果,我们还要再耐心的补点东西!下续