这节课完善_IMAGE_OPTIONAL_HEADER中的IMAGE_DATA_DIRECTORY成员和节表IMAGE_SECTION_HEADER部分 通过看可选头我们可以看到可选头的结尾是16个_IMAGE_DATA_DIRECTORY数组,这就是我们为什么在第30个成员中填入十六16个进制的10h,也就是十进制的16的原因了,代表有16个_IMAGE_DATA_DIRECTORY数组。 它的结构体成员如下图, 成员1表示该目录所指向的数据的内存偏移即RVA。成员2表示该目录所指向的内存数据的大小。DIRECTORY结构体数组中有16个这样的结构体 所有的宏名就是它的功能 我们在头文件中去验证一下
首先在helloword程序中声明一下这个结构体 右键这行代码,转到它的定义处 我们来到它的定义处,看到这些数组的宏定义 还可以在lordPE查看这些数组
点击目录按钮 然后到达目录表,是不是很爽啊?不但验证了结构体数组的正确性,还找到所有结构体数组成员的解释和内容,是不是感觉这lordPE也太强大了吧?(*^▽^*) 好了继续,问题来了,如果没有lordPE该怎么手动找啊?? 其实很简单,找到第一个节区头的name字段后,向上数128个字节,就是IMAGE_DATA_DIRECTORY数组啦 回归正题:成员31,128个字节,上面说过他是一个IMAGE_DATA_DIRECTORY结构的数组,通常具有16个元素。
IMAGE_DATA_DIRECTORY结构有两个成员,各占4个字节,那么也就得到成员31的总大小:2 * 4 * 16 = 128byte。16个元素中每个元素代表一个目录表,每个目录表表示的目录如下: 我们需要插入16*8=128个字节 我们的PE文件只是用来熟悉PE结构,所以除了导入表外其他的表我们都不去关心,查看导入表.rdata表在数组中的位置,由于我们还没有写导入表,我们还不知道他的位置,所以这里用’X’填充其位置。 终于完成了可选头的部分,下面该节区头了,先了解一下节区头 我们需要写入_IMAGE_SECTION_HEADER数据:,我们这里有3个段,.text(代码段), .rdata(只读数据段),data(全局变量数据段)。每段是一个IMAGE_SECTION_HEADER 结构,具有10个成员。
先来认识一下_IMAGE_SECTION_HEADER这个结构: 接下来我们动手来写.text段 我们只用到如下这些成员,其他成员不使用,填充0即可 成员1,8个字节,表识该段的名称,我们这里是.text,那么此值是他的ASKII码应该为“2E74657874000000”。 你以为这样写就对了吗?不,这才5个字节,后面还有3个字节呢!!这点特别容易错 成员2,4个字节,表示有效代码所占的字节数。我们这里所有代码数一下总共16h个,固此值为“16000000”。 成员3,4个字节,表示在.text段映射到内存中的起始地址,那么这个值如何得来呢?我们知道.text是紧跟PE结构后的,然后整个PE结构映射到内存后占的大小为1000h(因为PE结构小于1000h个字节,而对齐力度粒度是1000h),那么此值便得到了,“00100000”。 成员4,4个字节,表示.text段在文件中所占的大小。我们的实际代码只有16h个字节,那么这个值是不是16h呢?并不是,一定要注意段在文件中的对齐粒度是200h,所以此值为“00020000”。 成员5,4个字节,表示.text段在文件中的起始地址,上面已经计算过PE文件的总长度为400h,他实际上也就是.text的起始偏移地址,此值为“00040000”。 成员6,7,8,9,总共占12个字节,都仅用于目标文件,我们这里统统填为零。(仔细看结构体成员会发现是12个字节) 成员10,4个字节。包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。这个值实际上是二进制位进行或运算得到的值。各二进制位表示的意义如下:
不懂异或组合就看下图,每个属性对应的16进制加起来就行 其实转换很简单,第一个属性是第五个二进制位置1,算出来刚好是16进制的20,对应上图的第一个(很简单吧?????不懂真的是没救了哈) 其实用LordPE一键修改很方便,但是我后面才说就是为了先学懂原理嘛~~ 在我们这里,因为这是代码段,所以bit 5 (IMAGE_SCN_CNT_CODE)位置1,一般代码段都含有初始化数据,那bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA)位置1,有因为代码段的代码可以执行的,所以bit 29 (IMAGE_SCN_MEM_EXECUTE) 置1,那么这3个二进制位进行或运算最终得到此成员值“20000060”。
|