码迷,mamicode.com
首页 > 其他好文 > 详细

导入表结构复习 导入模块,函数名称,地址遍历

时间:2015-05-04 08:43:50      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:pe

关于PE结构导入表,以前只是手动分析,没有通过编程来实现。而且PE文件结构,不巩固的话,一段时间之后就会忘记,所以记录下这次试验,为IAT挂钩做好准备,也算是复习一下。

测试

环境:windows xp sp3
IDE: vs 2008 sp1
build:release


#include <windows.h>
#include <stdio.h>
#include <DbgHelp.h>
#pragma comment(lib,"dbghelp.lib")


int main()
{
    DWORD ModuleBase;
    ModuleBase = (DWORD)GetModuleHandle(NULL);
    DWORD dwNum=0;
    PIMAGE_IMPORT_DESCRIPTOR pImpDes=(PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData((PVOID)ModuleBase,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&dwNum);//IID
    printf("import_descriptor=%08x\n",pImpDes);
    while (pImpDes->Name)
    {
        printf("dll name is %s\n",pImpDes->Name+ModuleBase);
        PIMAGE_THUNK_DATA pINT_thunk =(PIMAGE_THUNK_DATA)(pImpDes->OriginalFirstThunk+ModuleBase);
        PIMAGE_THUNK_DATA pIAT_thunk =(PIMAGE_THUNK_DATA)(pImpDes->FirstThunk+ModuleBase);
        //while(thunk)
        while(pINT_thunk->u1.AddressOfData)
        {
            PIMAGE_IMPORT_BY_NAME pname = (PIMAGE_IMPORT_BY_NAME)(pINT_thunk->u1.AddressOfData+ModuleBase); 
            //printf("funcname=%s\n",pname->Name+ModuleBase);
            DWORD addr = (DWORD) pIAT_thunk->u1.Function;
            DWORD* where = &(pIAT_thunk->u1.Function);
            printf("funcname=%s \t  addr=%08x\t \t funaddr=%08x\n",pname->Name,where,addr);
            pINT_thunk++;
            pIAT_thunk++;
        }
        pImpDes++;
    }

    return 0;
}

总结

本来是打算用vc6.0 去完成的,但是dbghelp.h 提示找不到,就用vs了。

  • ImageDirectoryEntryToData 可以直接定位导入表描述地址,需要添加dbghelp.lib .
  • 一个DLL对应一个IID ,输入了多少dll,就有多少个IID 结构,这些IID在内存中连续排列,以一个字段全为0的IID表示结束。从IID 的数量可以看出导入了多少个DLL。
  • 同样的,INTIAT 也都是DWORD 大小的结构,导入了多少函数,就在内存中连续排列多少个,以一个字段为0的元素表示结束。 从IAT(或者INT)数组长度减去1可以得到导入的函数数目。
  • 上面已经说了,IID,INT,IAT 他们都是结构体数组,所以是连续排列的,不是链式结构。
  • 遍历导入的函数名称,不能从IAT上入手,因为一旦程序被windows加载器加载了,IAT处已经被加载成了真正的函数地址

关于代码的一些说明:

  • 需要注意在PE字段中哪些是RVA,哪些是VA。
  • //printf("funcname=%s\n",pname->Name+ModuleBase); 注意到我将这句给注释了,是因为理解错误。我本来以为name是个地址,RVA类型,所以要加基址。但是最后发现Byte Name[1] 中已经存放了字符串,而不是字符串的相对偏移指针,所以直接指向就行了。

  • 在写遍历的时候,犯了一个错误。就是对指针和内容没有区分开。while(pINT_thunk->u1.AddressOfData)是对的,但是我之前写成了while(pINT_thunk)PINT_thunk 是指针,是INT数组的地址,而不是INT数组的内容 ,使用错误的语句执行后会有异常,原因是到了INT为0 时,&INT(即pINT_thunk) 也不为0. INT结束的标志是内容为0,而不是指针为0.

验证

执行效果图
技术分享

在OD中测试,在数据窗口下ctrl+g 跳转到402000,右键选择长型->地址 ,即将数据解释为地址类型。
技术分享

从图中可以看出来,两个dll之间的IAT差了4字节,这4字节就是各自IAT数组的结束标志。
有意思的是实验中,不同的dll对应的INT和IAT基本上都是连续的,这是编译器做的,实际上每个dll的INT和IAT分别由各自IID中的字段来决定他们的位置(里面有各自的RVA指针),可以在内存中同一个段内任意分布。
但是IID数组在内存中一定是连续的

导入表结构复习 导入模块,函数名称,地址遍历

标签:pe

原文地址:http://blog.csdn.net/bugmeout/article/details/45461851

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!