标题:遍历PE导入表遇到问题 <<<问题已经解决>>>
只看楼主
redice
Rank: 3Rank: 3
等 级:新手上路
威 望:6
帖 子:902
专家分:0
注 册:2006-12-11
结帖率:72.73%
 问题点数:0 回复次数:5 
遍历PE导入表遇到问题 <<<问题已经解决>>>
我想同过遍历PE导入表,获得cmd.exe中使用到的DLL文件名,但是却得到了一堆无意义的字符....请高手指点

我的代码如下:

#include <windows.h>
#include <stdio.h>

//如果是全零则返回TRUE,否则返回FALSE
BOOL allzero(BYTE data[],int datasize)
{
    int i=0;
    for(i=0;i<datasize;i++)
    {
        if(data[i]) return FALSE;
    }
    return TRUE;
}

BOOL ReadPeHeader(char FileName[MAX_PATH])
{

    IMAGE_DOS_HEADER image_dos_header;
    IMAGE_NT_HEADERS image_nt_header;

    DWORD dwRead;
    HANDLE hFile;
    long peoffset;
    
    hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE==hFile)    return FALSE;

    ReadFile(hFile,&image_dos_header,sizeof(image_dos_header),&dwRead,NULL);
    
    if(dwRead==sizeof(image_dos_header))
        //PE header偏移量,相对于文件首部
        peoffset=image_dos_header.e_lfanew;
    else
        return FALSE;

    SetFilePointer(hFile,peoffset,NULL,FILE_BEGIN);
    ReadFile(hFile,&image_nt_header,sizeof(image_nt_header),&dwRead,NULL);
   
    if(dwRead==sizeof(image_nt_header))
    {
        //导入表的rav
        DWORD import_table_rav;
        //导入表相对文件起始的偏移
        DWORD improt_table_offset;
        IMAGE_IMPORT_DESCRIPTOR image_import_descriptor;
        IMAGE_SECTION_HEADER image_section_header;
        int i;
        int sectionsnum;

        //data directory数组第二项的VirtualAddress包含引入表的rav
        import_table_rav=image_nt_header.OptionalHeader.DataDirectory[1].VirtualAddress;
        
        //确定导入表相对文件的偏移地址
        sectionsnum=image_nt_header.FileHeader.NumberOfSections;
        SetFilePointer(hFile,peoffset+sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
        for(i=0;i<sectionsnum;i++)
        {
            ReadFile(hFile,&image_section_header,sizeof(image_section_header),&dwRead,NULL);
            if(dwRead==sizeof(IMAGE_SECTION_HEADER))
            {
              if((import_table_rav>=image_section_header.VirtualAddress) &&
                  (import_table_rav<=(image_section_header.VirtualAddress+image_section_header.SizeOfRawData)))
              {
                  improt_table_offset=image_section_header.PointerToRawData+import_table_rav-image_section_header.VirtualAddress;
                  printf("section name: %s\n",&image_section_header.Name);
                  break;
              }
            }
            else
                return FALSE;
        }
        i=-1;
        while(1)
        {
            i++;
            SetFilePointer(hFile,improt_table_offset+sizeof(image_import_descriptor)*i,NULL,FILE_BEGIN);
            ReadFile(hFile,&image_import_descriptor,sizeof(image_import_descriptor),&dwRead,NULL);
            if(dwRead==sizeof(image_import_descriptor))
            {
                IMAGE_IMPORT_BY_NAME image_import_by_name;

                if(allzero((BYTE *)&image_import_descriptor,dwRead)) break;
                printf("dll filename: %s\n",&image_import_descriptor.Name);
            }
            else
                return FALSE;
        }
    }
    else
        return FALSE;
    CloseHandle(hFile);
    return TRUE;
}

int main()
{
    ReadPeHeader("c:\\windows\\system32\\cmd.exe");
    return 0;
}


[[it] 本帖最后由 redice 于 2008-6-5 14:47 编辑 [/it]]
搜索更多相关主题的帖子: 遍历 help 
2008-06-04 21:33
Arcticanimal
Rank: 3Rank: 3
等 级:论坛游民
威 望:7
帖 子:341
专家分:20
注 册:2007-3-17
得分:0 
代码有错误,没细看,MS搞得也很复杂.
要指出的是, 相对虚拟地址仅在文件以PE文件映像加载到系统后才有效.CreateFile是不行的,LoadLibrary.
RVA加上基址得到线性地址.

try new catch
2008-06-04 21:46
redice
Rank: 3Rank: 3
等 级:新手上路
威 望:6
帖 子:902
专家分:0
注 册:2006-12-11
得分:0 
[bo][un]Arcticanimal[/un] 在 2008-6-4 21:46 的发言:[/bo]

代码有错误,没细看,MS搞得也很复杂.
要指出的是, 相对虚拟地址仅在文件以PE文件映像加载到系统后才有效.CreateFile是不行的,LoadLibrary.
RVA加上基址得到线性地址.



相对虚拟地址是可以转化为文件内的偏移地址的:
offset=rav - Section.VirtualAddress + Section.PointerToRawData;

其中Section是var所在的节(可以通过遍历所有的节,并通过比较var与VirtualAddress的大小确定var所在的节的)

鲲鹏数据 - 专业Web数据采集服务提供者
http://www.
2008-06-04 22:08
redice
Rank: 3Rank: 3
等 级:新手上路
威 望:6
帖 子:902
专家分:0
注 册:2006-12-11
得分:0 
映射到内存还是不对呀 和上面的代码效果是一样

// ImportTable
#include <windows.h>
#include <stdio.h>

//判断指定缓冲区是否为全0
//全零:TRUE 非全零:FALSE
BOOL allzero(BYTE data[],int datasize)
{
    int i=0;
    for(i=0;i<datasize;i++)
    {
        if(data[i]) return FALSE;
    }
    return TRUE;
}

//根据相对虚拟(rva)地址计算偏移地址(offset)
UINT rva2offset(IMAGE_NT_HEADERS * pimage_nt_header,UINT rva)
{
    IMAGE_SECTION_HEADER *pimage_section_header;
    UINT sectionnum,i;
    //取得节表项数目
    sectionnum=pimage_nt_header->FileHeader.NumberOfSections;
    //取得第一个节表项
    pimage_section_header=(IMAGE_SECTION_HEADER *)
                          ((BYTE *)pimage_nt_header+sizeof(IMAGE_NT_HEADERS));
    for(i=0;i<sectionnum;i++)
    {
        if((pimage_section_header->VirtualAddress<rva)
            &&rva<(pimage_section_header->VirtualAddress+pimage_section_header->SizeOfRawData))            
        {
            printf("section: %s\n",&pimage_section_header->Name);
            return rva-pimage_section_header->VirtualAddress+pimage_section_header->PointerToRawData;
        }
        pimage_section_header++;    
    }
    return 0;
}

BOOL ReadImportTable(char FileName[MAX_PATH])
{

    IMAGE_DOS_HEADER *pimage_dos_header;
    IMAGE_NT_HEADERS *pimage_nt_header;
    IMAGE_IMPORT_DESCRIPTOR *pimage_import_descriptor;
    IMAGE_IMPORT_BY_NAME *pimage_import_by_name;

    HANDLE hFile;
    HANDLE hMapping;
    LPVOID pMapping;
    ULONG rva_ofimporttable,offset_importtable;

    hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE==hFile)    return FALSE;
    
    //将PE文件映射到内存
    hMapping=CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,0);
    if(!hMapping) return FALSE;

    pMapping=MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0);
    if(!pMapping) return FALSE;

    //取得dos_header的地址
    pimage_dos_header=(IMAGE_DOS_HEADER *)pMapping;
    if(pimage_dos_header->e_magic!=IMAGE_DOS_SIGNATURE)
    {
        printf("无效的PE文件!\n");
        return FALSE;
    }
    //计算nt_header的地址
    pimage_nt_header=(IMAGE_NT_HEADERS *)((BYTE *)pMapping+pimage_dos_header->e_lfanew);
    if(pimage_nt_header->Signature!=IMAGE_NT_SIGNATURE)
    {
        printf("无效的PE文件!\n");
        return FALSE;
    }
    //导入表的相对虚拟地址(RVA)
    rva_ofimporttable=pimage_nt_header->OptionalHeader.DataDirectory[1].VirtualAddress;
    //根据相对虚拟(rva)地址计算偏移地址(offset)
    offset_importtable=rva2offset(pimage_nt_header,rva_ofimporttable);
    if(!offset_importtable) return FALSE;
   
    //取得导入表的地址
    pimage_import_descriptor=(IMAGE_IMPORT_DESCRIPTOR *)((BYTE *)pMapping+offset_importtable);
    while(!allzero((BYTE *)pimage_import_descriptor,sizeof(IMAGE_IMPORT_DESCRIPTOR)))
    {
        printf("dll filename: %s\n",&pimage_import_descriptor->Name);
        pimage_import_descriptor++;
    }
    return TRUE;
}

int main()
{
    ReadImportTable("c:\\windows\\system32\\cmd.exe");
    return 0;
}

鲲鹏数据 - 专业Web数据采集服务提供者
http://www.
2008-06-05 12:21
redice
Rank: 3Rank: 3
等 级:新手上路
威 望:6
帖 子:902
专家分:0
注 册:2006-12-11
得分:0 
问题终于搞定了,原因在于,我把IMAGE_IMPORT_DESCRIPTOR结构体重的Name当作字符串处理了,其实它是一个执行dll字符串的rva值.

总结一下:
读取PE文件的信息,并不一定要先将PE文件映射到内存中。通过移动文件读写指针同样可以达到PE文件任何信息(当然包括导入表)的目的。其实将文件映射到内存中,文件的数据并没有发生改变(PE文件被装载器装在到内存中时才会发生变化)。因此读内存中的文件映射和读硬盘上的文件数据是一模一样的。不过我还是建议先将文件映射到内存中,因为频繁的移动文件读写指针,读取数据真的很讨厌。在内存中就方便多了,指针做个运算就完成了。

rva转offset真的很麻烦,不过知道原理后,还是挺简单的。我说一下思路:
1 通过dos_header获取nt_header的offset;
2 通过nt_header.FileHeader.NumberOfSections获取PE中节的数目;
3 PE节表就紧跟在nt_header后面,遍历PE节表,如果rva>=section.VirtualAddress且
  rva<section.VirtualAddress+section.SizeOfRawData那么就可以确定该rva是落在该节中了
4 section是上面所确定的节,rva-section.VirtualAddress+section.PointerToRawData就是rva对应的offset值


最后附上我的完整程序,本来只想显示一下dll filename,后来又把api也显示了出来...
整整花了我一天的时间呀。

程序有不足之处,还望高手指点...




//by redice redice@  http://blod.    2008.6.5
//转载请保留上述信息 谢谢合作
// ImportTable
#include <windows.h>
#include <stdio.h>

//判断指定缓冲区是否为全0
//全零:TRUE 非全零:FALSE
BOOL allzero(BYTE data[],int datasize)
{
    int i=0;
    for(i=0;i<datasize;i++)
    {
        if(data[i]) return FALSE;
    }
    return TRUE;
}

//根据相对虚拟(rva)地址计算偏移地址(offset)
UINT rva2offset(IMAGE_NT_HEADERS * pimage_nt_header,UINT rva)
{
    IMAGE_SECTION_HEADER *pimage_section_header;
    UINT sectionnum,i;
    //取得节表项数目
    sectionnum=pimage_nt_header->FileHeader.NumberOfSections;
    //取得第一个节表项
    pimage_section_header=(IMAGE_SECTION_HEADER *)
                          ((BYTE *)pimage_nt_header+sizeof(IMAGE_NT_HEADERS));
    for(i=0;i<sectionnum;i++)
    {
        if((pimage_section_header->VirtualAddress<=rva)
            &&rva<(pimage_section_header->VirtualAddress+pimage_section_header->SizeOfRawData))    
            return rva-pimage_section_header->VirtualAddress+pimage_section_header->PointerToRawData;
        pimage_section_header++;    
    }
    return 0;
}

BOOL ReadImportTable(char FileName[MAX_PATH])
{

    IMAGE_DOS_HEADER *pimage_dos_header;
    IMAGE_NT_HEADERS *pimage_nt_header;
    IMAGE_IMPORT_DESCRIPTOR *pimage_import_descriptor;
    IMAGE_THUNK_DATA *pimage_thunk_data;
    IMAGE_IMPORT_BY_NAME *pimage_import_by_name;

    HANDLE hFile;
    HANDLE hMapping;
    LPVOID pMapping;
    ULONG rva_ofimporttable,offset_importtable,rva_name,offset_name;
    ULONG rva_image_thunk_data,offset_image_thunk_data;
    ULONG rva_image_import_by_name,offset_image_import_by_name;

    char *pname;

    hFile=CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE==hFile)    return FALSE;
    
    //将PE文件映射到内存
    hMapping=CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,0);
    if(!hMapping) return FALSE;

    pMapping=MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0);
    if(!pMapping) return FALSE;

    //取得dos_header的地址
    pimage_dos_header=(IMAGE_DOS_HEADER *)pMapping;
    if(pimage_dos_header->e_magic!=IMAGE_DOS_SIGNATURE)
    {
        printf("无效的PE文件!\n");
        return FALSE;
    }
    //计算nt_header的地址
    pimage_nt_header=(IMAGE_NT_HEADERS *)((BYTE *)pMapping+pimage_dos_header->e_lfanew);
    if(pimage_nt_header->Signature!=IMAGE_NT_SIGNATURE)
    {
        printf("无效的PE文件!\n");
        return FALSE;
    }
    //导入表的相对虚拟地址(RVA)
    rva_ofimporttable=pimage_nt_header->OptionalHeader.DataDirectory[1].VirtualAddress;
    //根据相对虚拟(rva)地址计算偏移地址(offset)
    offset_importtable=rva2offset(pimage_nt_header,rva_ofimporttable);
    if(!offset_importtable) return FALSE;
   
    //取得导入表的地址
    pimage_import_descriptor=(IMAGE_IMPORT_DESCRIPTOR *)((BYTE *)pMapping+offset_importtable);
    while(!allzero((BYTE *)pimage_import_descriptor,sizeof(IMAGE_IMPORT_DESCRIPTOR)))
    {
        //注意:这里pimage_import_descriptor->Name也是一个rva值,不是一个字符串
        rva_name=pimage_import_descriptor->Name;
        offset_name=rva2offset(pimage_nt_header,rva_name);
        pname=(BYTE *)pMapping+offset_name;
        printf("\nDll FileName: %s\n\n",pname);
        //指向一个 IMAGE_THUNK_DATA 结构数组的RVA
        rva_image_thunk_data=pimage_import_descriptor->OriginalFirstThunk;
        if(!rva_image_thunk_data)
            rva_image_thunk_data=pimage_import_descriptor->FirstThunk;
        //指向一个 IMAGE_THUNK_DATA 结构数组的offset
        offset_image_thunk_data=rva2offset(pimage_nt_header,rva_image_thunk_data);
        //指向一个 IMAGE_THUNK_DATA 结构数组的指针
        pimage_thunk_data=(IMAGE_THUNK_DATA *)((BYTE *)pMapping+offset_image_thunk_data);
        while(pimage_thunk_data->u1.Ordinal)
        {
            /*如果pimage_thunk_data->u1.Ordinal的最高二进位为1,
            那么函数是由序数引入的,可以从该值的低字节提取序数。*/
            if((pimage_thunk_data->u1.Ordinal)&IMAGE_ORDINAL_FLAG32)
                printf("          %x (ord.)\n",(pimage_thunk_data->u1.Ordinal)& 0x0FFFF);
            else/*如果元素值的最高二进位为0,就可将该值作为RVA转入IMAGE_IMPORT_BY_NAME结构,
                跳过 Hint 就是函数名字了。*/
            {
                //取得IMAGE_IMPORT_BY_NAME结构的rva
                rva_image_import_by_name=pimage_thunk_data->u1.Ordinal;
                //取得IMAGE_IMPORT_BY_NAME结构的offset
                offset_image_import_by_name=rva2offset(pimage_nt_header,rva_image_import_by_name);
                //取得IMAGE_IMPORT_BY_NAME结构的指针
                pimage_import_by_name=(IMAGE_IMPORT_BY_NAME *)((BYTE *)pMapping+offset_image_import_by_name);
                printf("         %s\n",&pimage_import_by_name->Name);
            }
              pimage_thunk_data++;
        }
        pimage_import_descriptor++;
    }
    return TRUE;
}

int main()
{
    ReadImportTable("c:\\windows\\system32\\cmd.exe");
    return 0;
}


[[it] 本帖最后由 redice 于 2008-6-5 14:30 编辑 [/it]]

鲲鹏数据 - 专业Web数据采集服务提供者
http://www.
2008-06-05 14:28
zhang88766
Rank: 1
等 级:新手上路
帖 子:1
专家分:0
注 册:2009-1-16
得分:0 
楼主注意:更改后的程序还有错误:pname=(BYTE *)pMapping+offset_name;应该写成这样吧pname=(char *)(BYTE *)pMapping+offset_name;我的编译环境是:VC6.0,WINDOWS XP SP3
2009-10-07 15:41



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-217687-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.025019 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved