第八课 写入导入表

这节课我们来完成.rdata段,这个段非常重要,也有些繁琐。之前我们只是简单的完成数据目录数组的第二个元素导入表目录,用’x’为标记,填充了导入表的位置,现在我们要一并解决这个问题。前面已经说过,每个数据目录具有两个成员,第一个成员表示目录表的起始RVA地址,第二个成员表示目录表的长度。对于我们这个导入表目录来说,他指的就是导入表了,这个导入表实际上是一个IMAGE_IMPORT_DESCRIPTOR 结构数组,每个结构包含PE文件引入函数的一个相关DLL的信息。比如,如果该PE文件从10个不同的DLL中引入函数,那么这个数组就有11个成员。第11个数组以一个全0的成员结尾。那么.rdata段的内容应该就是IMAGE_IMPORT_DESCRIPTOR 结构数组了,我们这里用到user32.dll库,第一个元素的成员1的地址是不是就是.rdata的首地址呢?不是这样的,这里有个规律,我们导入了多少个函数,那么就要空出8乘以导入函数个数个字符的空间(为什么要空些空间呢?要放什么呢?一会便可知晓),在其后才是IMAGE_IMPORT_DESCRIPTOR结构成员的首地址。在这里我们导入了1个函数,那么应该空1 * 8 = 8个字符,.rdata段的首地址是600h,那么IMAGE_IMPORT_DESCRIPTOR结构成员的首地址应该是608h。

   这时我们便得到了数据目录数组的第二个元素导入表目录结构成员1的值,是不是就是“08060000”呢?

     不是的,因为这里的地址是RVA(映射后内存的地址),而文件的600h被映射为2000h(LordPE查看),所以608h被映射后的RVA为2008h,故此值为“08200000”

我们可以把其中的“xxxxxxxx”替换为“08200000”。

数据目录数组的第二个元素导入表目录结构成员2的值是指IMAGE_IMPORT_DESCRIPTOR 结构数组的长度,IMAGE_IMPORT_DESCRIPTOR 结构长度是20个字节,我们这里的IMAGE_IMPORT_DESCRIPTOR 结构数组元素是1个,并且还有一个以全零结尾的结构,那么总长度就是2 * 20 = 40byte,转换成十六进制是28,我们可以把另一个“xxxxxxxx”替换为“28000000”。

替换完成如下图

成员1,4个字节,他实际上是指向一个 IMAGE_THUNK_DATA 结构数组的RVA,而IMAGE_THUNK_DATA 结构数组记录所有从某个.dll库中导入函数名称的RVA。IMAGE_THUNK_DATA 结构位于IMAGE_IMPORT_DESCRIPTOR 结构之后,由IMAGE_IMPORT_DESCRIPTOR 结构数组的起始地址和长度可以得到IMAGE_THUNK_DATA 结构数组的起始地址。即608h + 28h =630h,那么这个IMAGE_IMPORT_DESCRIPTOR 结构数组的第一个元素的成员1的值是不是就是630h呢?并不是的,因为这是一个RVA值,所以映射后的值应该是“30200000”。

 

成员2,成员3各4各字节,用处不大,我们用零填充。

成员4,4个字节,是指向DLL名字的RVA,即指向DLL名字的指针,也是一个ASCIIZ字符串。我们将在后面编写“user32.dll”字符串,这个值不是固定的值,只要将这个字符串写入这个区段中,并计算RVA填入后,程序都能正确的运行,我们这里按照之前vc编译 的小demo的地址填写为:”46200000”。


成员5,4个字节,指向一个 IMAGE_THUNK_DATA 结构数组的RVA,同成员1一样,但是此IMAGE_THUNK_DATA 数组结构保存了所有导入函数的指针。PE文件被装载到内存时,用引入函数真实地址来替代由这里的IMAGE_THUNK_DATA 数组里的元素值。那么这里填哪里的地址呢?这就要用到我们刚刚空下的空间了。我们将此值设为“00200000”。

最后以全零结尾这个IMAGE_IMPORT_DESCRIPTOR 结构数组,即:“00000000”,“00000000”,“00000000”,“00000000”,“00000000”。 共20个字节。

接下来写入导入函数名称表,这个值的位置也是可以写在这个段中的任意位置,

但为了同vc的风格一致,我们参考下1.exe这个原程序文件。查看1.exe这个文件大家会发现这里导入的函数名并非单纯的”MessageBoxA”前面还多了两个字节,这也是我上一节课中留下的一个知识没有讲解,为了了解这两个字节的意义我们必须了解IMAGE_IMPORT_BY_NAME这个结构:

成员1: WORD    Hint;指示本函数在其所驻留dll的输入表中的序号。该域被PE装载器

用来在DLL的输出表里快速查询函数。该值不是必须的,一些链接器将此值设为0.

成员2:BYTE    Name[1];含有输入函数的函数名,函数名是一个ASCII码字符串。

看完这个结构后我们就能理解MessageBoxA前面的两个字节数据表示什么了,这个值可以为0,这里我们参考1.exe写入他的值。

在文件中写入”MessageBoxA”字串。和他前面的数据。紧接着MessageBoxA后是导入的DLL名称。在文件中写入”user32.dll”。他的在导入表中的位置我们已经填写过了。

 

填写OriginalFirstThunk中的IMAGE_THUNK_DATA 数据,这个数据指向IMAGE_IMPORT_BY_NAME的RVA。

填写FirstThunk中的IMAGE_THUNK_DATA 数据,这个数据也指向IMAGE_IMPORT_BY_NAME的RVA。

至此整个导入表的部分就填写完毕了,我们用LordPE查看一下我们的PE文件。

用Lord也可以看到导入表的函数了


 

 

猜你喜欢

转载自blog.csdn.net/qq_41917908/article/details/81212717