首页
社区
课程
招聘
[求助]关于定位IAT的一个函式的疑问
发表于: 2008-4-30 22:46 6639

[求助]关于定位IAT的一个函式的疑问

2008-4-30 22:46
6639
最近刚刚开始看pe文件,有一个关于定位IAT的程序,其中的代码需要一段RVAToOffset的函数。

贴上我在qq群问的与别人的讨论的文字,大家看一下

====================================

我:21:57:08
问个问题把?pe分析软件分析pe文件和我直接运行pe文件有什么区别啊?
邪灵亡()  21:57:42
好问题
:21:57:55
我是被pe搞糊涂了
邪灵亡()  21:58:32
分析软件只是把pe读到内存里,并没有运行
邪灵亡()  21:58:52
直接运行pe文件,会读到内存里,并让cpu去执行

我:22:00:05
那这这个映射完成后和我直接运行后在内存中的结构布局一样吗
22:00:26
就是牵扯到IMAGE BASE之类的东西
邪灵亡()  22:00:56
image base只是个数字而已
我:22:01:48

我:22:03:23
pe文件直接映射在内存  和  windows pe装载器装载pe文件 两种方式 对比,内存布局一样吗?
就是是不是都是4k对其之类的
邪灵亡()  22:05:43
我晕,太细节了,咱不懂
邪灵亡()  22:05:50
反正差不多就那4k撒的了
我:22:05:50


邪灵亡()  22:06:20
关注的应该是那个virtualalign和filealign的区别,我还没搞懂那个关系
我:22:06:38
那个关系我明白
邪灵亡()  22:07:01
撒关系,讲解下
我:22:07:33
file应该是扇区的大小,512字节
我:22:07:49
vitual对其应该是内存的页文件大小
我:22:08:12
磁盘结构中512字节(一个扇区)是最小的存取单位(不同的操作系统可能不同,windows是以簇为单位的)
邪灵亡()  22:08:18
就是对一个文件,为撒virtualsize和rawsize不同
我:22:08:53
rawsize是对其后的,而那个vitualsize据试验应该是对其以前的大小
我:22:09:12
我在问个问题吧?那你怎么理解“pe文件的结构和在内存中的数据结构是一致”的啊?
我:22:09:26
这里的数据结构你是怎么理解的啊?
邪灵亡()  22:10:58
它放在那里就是让你去用的
邪灵亡()  22:10:59
没怎么理解
我:22:11:05

我:22:11:38
那你分析过一个分析导入表的程序吗?
邪灵亡()  22:11:39
那个virtualsize是对其前的? 为撒还弄个virtualalign?
我:22:11:50
这就不知道了
邪灵亡()  22:12:06
不过忘记了,要查的话,我记得起来
邪灵亡()  22:12:17
那个加密与解密上讲的很清楚
邪灵亡()  22:12:19
分析过
我:22:12:44
就是有一部分, RVAToOffset()函数,我不是很理解
我:22:13:05
为什么要RAV2文件偏移啊?
邪灵亡()  22:13:08
函数?

我:22:13:35
int main(int argc, char* argv[])
{
        //打开文件
        HANDLE hFile=CreateFile(argv[1],GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
        if(hFile==INVALID_HANDLE_VALUE)
        {
                printf("CreateFile Failed\n");
                return 0;
        }

        HANDLE hMap=CreateFileMapping(hFile,NULL,PAGE_READONLY,NULL,NULL,NULL);
        if(hMap==INVALID_HANDLE_VALUE)
        {
                printf("CreateFileMapping Failed\n");
                return 0;
        }
       
        LPVOID lpBase=MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);
        if(lpBase==NULL)
        {
                printf("MapViewOfFile Failed\n");
                return 0;       
        }
        IMAGE_DOS_HEADER *dosHeader;
        IMAGE_NT_HEADERS *ntHeader;
        IMAGE_IMPORT_BY_NAME *ImportName;
       
        dosHeader=(IMAGE_DOS_HEADER*)lpBase;
       
        if (dosHeader->e_magic!=IMAGE_DOS_SIGNATURE)
        {
                printf("This is not a windows file\n");
                return 0;
        }
        //定位到PE header
        ntHeader=(IMAGE_NT_HEADERS*)((BYTE*)lpBase+dosHeader->e_lfanew);
        if(ntHeader->Signature!=IMAGE_NT_SIGNATURE)
        {
                printf("This is not a win32 file\n");
                return 0;
        }
        //定位到导入表
        IMAGE_IMPORT_DESCRIPTOR *ImportDec=(IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)lpBase+RVAToOffset(lpBase,ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
        while(ImportDec->FirstThunk)
        {
                //得到DLL文件名
                char *pDllName=(char*)((BYTE*)lpBase+RVAToOffset(lpBase,ImportDec->Name));
                printf("\nDLL文件名:%s\n",pDllName);
我:22:13:45
char *pDllName=(char*)((BYTE*)lpBase+RVAToOffset(lpBase,ImportDec->Name));
                printf("\nDLL文件名:%s\n",pDllName);

我:22:13:57
你只看这一段就行了
我:22:14:08
里面有一个RVAToOffset的函数
我:22:14:22
DWORD RVAToOffset(LPVOID lpBase,DWORD VirtualAddress)
{
        IMAGE_DOS_HEADER *dosHeader;
        IMAGE_NT_HEADERS *ntHeader;
        IMAGE_SECTION_HEADER *SectionHeader;
        int NumOfSections;
        dosHeader=(IMAGE_DOS_HEADER*)lpBase;
        ntHeader=(IMAGE_NT_HEADERS*)((BYTE*)lpBase+dosHeader->e_lfanew);
        NumOfSections=ntHeader->FileHeader.NumberOfSections;
        for (int i=0;i<NumOfSections;i++)
        {
                SectionHeader=(IMAGE_SECTION_HEADER*)((BYTE*)lpBase+dosHeader->e_lfanew+sizeof(IMAGE_NT_HEADERS))+i;
                if(VirtualAddress>SectionHeader->VirtualAddress&&VirtualAddress<SectionHeader->VirtualAddress+SectionHeader->SizeOfRawData)
                                                                                                                                                      
                                                                                                   
                                   {
                        DWORD AposRAV=VirtualAddress-SectionHeader->VirtualAddress;
                        DWORD Offset=SectionHeader->PointerToRawData+AposRAV;
                        return Offset;
                }
        }
        return 0;
}

邪灵亡()  22:14:25
RVAToOffset这是别人自己写的吧?
我:22:14:36

我:22:14:44
罗云彬的那本书里面也有的
我:22:14:49
只是用汇编写的
我:22:14:58
我看了一下和这段代码是一个意思
我:22:15:39
我就是不明白为什么用映射文件分析(也就是pe分析软件的做法)为什么还要那个函数啊?
邪灵亡()  22:18:02
函数里面的参数是DWORD VirtualAddress,怎么传的时候是传的ImportDec->Name
我:22:18:43
那个name成员就是以RAV存在的啊
我:22:18:52
定义是这个样子的
我:22:19:33
有一片文章说是文件映射和文件中的数据结构是一致的。而那个RAV是在pe装载器装载后的结果,所以才需要地址转换
我:22:19:50
我想了好半天
邪灵亡()  22:20:29
是的,RVA就是装载后的结果
邪灵亡()  22:20:36
你的程序是想得到什么
我:22:20:43
文件映射也是把pe文件映射到内存中,数据结构怎么会和文件中的一样呢?至少对其大小是不一样的。
我:22:21:02
我就是不明白这里的地址转换的作用。
我:22:21:44
既然都是在内存中,一个是文件映射,一个是装载器装载,都是在内存中,为什么还需要地址换呢?
邪灵亡()  22:21:55
你说rva?
我:22:21:59

我:22:22:37
我感觉应该不用那个地址转换的,但是我见到的用文件映射方式的文章貌似都是这样的
我:22:22:52
至少罗云彬和看雪的书中是这样的
邪灵亡()  22:23:06
rva是个偏移,你MapViewOfFile后得到一个地址,这个地址加偏移就是实际在内存中的地址
我:22:23:40
恩,确切的说应该是线性地址
我:22:24:07
这一点我理解
我:22:25:03
那那个地址转换你怎么理解呢?
我:22:25:23
就是那个函数完成的功能
我:22:25:34
都是在内存中,为什么还需要地址换呢?



=====================
有关疑问我已经用黑色字体标出来了。
都是在内存中,为什么还需要地址换呢?

大家帮帮忙解释一下吧?谢谢了

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
2
内存映射文件和PE装载虽然都是将PE文件装入内存,不过,PE装载器将PE文件装入内存虽然没有改变PE文件的结构,但装载器对文件进行过对齐操作,每个PE节区以1000为对齐,改变各个节区的偏移地址,这一点和内存映射不同,内存映射并不对PE文件进行PE节区对齐。其文件内容偏移和磁盘文件里是一样的。而IAT所保存的地址是PE装载后对齐了节区之后的偏移,和磁盘文件的偏移不同,所以需要进行RavToOffset的转换。
2008-5-1 00:14
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
你说的我理解了,还有几点不理解:
1.具体的PE装载器将PE文件装入内存没有改变PE文件的结构是指什么,是pe结构的各个成员大小没有改变(这是肯定的了),还包括什么?比如成员之间的间距之类的。。。

2.假如我的pe文件大于4k(不妨设为5k),文件映射是以4k(x86结构的内存页文件大小)为基本单位的,这个时候,pe文件是怎么 映射到内存中的呢?是文件的前4k映射到第一个页文件,文件的后1k映射到第二个页文件的前1k(也就是说映射的时候和pe文件的结构是一样的)吗?(我们暂且假设系统把整个pe文件全部映射到内存中)

3.比如我自己写一个pe分析工具,我在操作的时候,是用文件映射的方式读取的吧,但是这时候我怎么还能够看出来某些结构的RVA呢?(这时候pe装载器还没有装载pe文件,应该还不知道imagebase那,毕竟imagebase可以自己指定)
既然windows pe装载器没有载入,那么我怎么能够确定某一结构的RVA呢?

希望给以指点,谢谢
2008-5-1 00:52
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
4
你说的我理解了,还有几点不理解:
1.具体的PE装载器将PE文件装入内存没有改变PE文件的结构是指什么,是pe结构的各个成员大小没有改变(这是肯定的了),还包括什么?比如成员之间的间距之类的。。。

是指PE的各个结构成员位置间距偏移位置并没有改变。

2.假如我的pe文件大于4k(不妨设为5k),文件映射是以4k(x86结构的内存页文件大小)为基本单位的,这个时候,pe文件是怎么 映射到内存中的呢?是文件的前4k映射到第一个页文件,文件的后1k映射到第二个页文件的前1k(也就是说映射的时候和pe文件的结构是一样的)吗?(我们暂且假设系统把整个pe文件全部映射到内存中)

PE文件在编译器编译后,是以4K为对齐的,不足4K的地方会以0填充位置。同样,PE文件被PE装载器加载到内存,也是以4k为对齐单位。不过,PE装载到内存之后每个段节区会被以1000kb为区分段对齐的,如:PE头保存在偏移0-1000 的位置(PE头数据不足1000kb大小的话PE装载器会以0填充到1000以对齐),代码段保存在内存偏移的1000-2000或更多(如果代码段的数据也不足1000单位大小的话和前面一样,PE装载器也会以0填充到1000单位的大小)(注:代码段并没有规定一定会保存在1000-2000之后的位置,资源段或其他段也可能保存在这个位置,我这里只是代码段加以说明一下而已)。这就是磁盘文件为什么要对PE装载器装载的PE必须转换RVA的原因。

3.比如我自己写一个pe分析工具,我在操作的时候,是用文件映射的方式读取的吧,但是这时候我怎么还能够看出来某些结构的RVA呢?(这时候pe装载器还没有装载pe文件,应该还不知道imagebase那,毕竟imagebase可以自己指定)
既然windows pe装载器没有载入,那么我怎么能够确定某一结构的RVA呢?

PE有相关的结构成员有保存着其相关结构的RVA,只要读取这些结构就可以知道,详细信息看PE结构说明。

希望给以指点,谢谢
2008-5-1 09:28
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我晓得了,多谢虾哥指点
2008-5-1 19:42
0
游客
登录 | 注册 方可回帖
返回
//