欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]Pe解析总结

[原创]Pe解析总结

前言   
    一个操作系统的可执行文件格式在很多方面是这个系统的一面镜子。虽然学习一个可执行文件格式通常不是一个程序员的首要任务,但是你可以从这其中学到大量的知识。在可预知的未来,包括, PE文件格式在 MicroSoft 的操作系统中扮演一个重要的角色。如果你在使用 Win32 或 Winnt ,那么你已经在使用 PE 文件了。甚至你只是在 Windows3.1 下使用 Visual C++编程,你使用的仍然是 PE 文件(Visual C++ 的 32 位扩展组件用这个格式)。简而言之,PE 格式已经普遍应用,并且在不短的将来仍是不可避免的。PE结构如此重要,我们可以通过写一个PE_EDIT 工具来理解pe文件,这是本文的目的(15pb项目)
                                                          总体设计
      以简洁,直观的设计理念。我以树控件为主要控件,如下图:   
[图片:emp.gif]
当然,树控件总体展现能力强,具体数据展示能力比较弱。为了弥补这一缺陷,我加了右键菜单功能。鼠标点击树控件哪一项,来展示具体数据。
为了数据和界面分离,可以写个数据类,这里是CPeTool。所有的数据组织都在这个类里面,当然这个pe工具所有的逻辑都在这个类里面

class PeTool

{

private:

        MAP_FILE_STRUCT m_filestruct;

public:

        vector<LPVOID>  m_importVector;

        vector<DOSHEADER*> m_DosHeaderVector;

        vector<DOSHEADER*> m_FileHeaderVector;

        vector<DOSHEADER*> m_OptionHeaderVector;

        vector<SECTION*>        m_SectionVector;

        vector<EXPORT_FUNC_DESCRIPTOR> m_ExportFucDecVector;

        vector<IMPORT_TABLE> m_ImportTableVector;

        IMAGE_IMPORT_DESCRIPTOR m_Image_Import;

        IMAGE_EXPORT_DIRECTORY m_Image_Export;

        DWORD m_exportOffset;

        bool m_isHasExport;

        bool m_isHaxImport;

public:

   PeTool();

        ~PeTool();

        TREENODE* m_pHeadNode;

        TREENODE* m_pResourceHeadNode;

        bool LoadFile(LPSTR lpFileName);

        //是否为PE

        bool IsPe();

        //我用的是树形框,构造树,初始化后,此数据初始化树形框

        void CreateTree();

        //创建资源数

        void CreateResourceTree();

        void AddNodeToResourceTree(TREENODE*,PIMAGE_RESOURCE_DIRECTORY,int depath);

        void PreAddNode(TREENODE*,int&);

        //释放空间

        void DeleteTree(TREENODE*);

        //分析dosheader

        void DosHeaderAnalysis();

        //分析Fileheader

        void FileHeaderAnalysis();

        //分析Optionheader

        void OptionHeaderAnalysis();

        //分析Section

        void SectionAnalysis();

        //分析EXport_Table

        void ExportTableAnalysis();

        //分析Import_table

        void ImportTableAnalysis();

        //得到资源指针

        DWORD GetResourceOffset();

        PIMAGE_DOS_HEADER GetDosHeaderPt();

        //地址转换

        DWORD RSAtoOffset(DWORD);

        //得到NT指针

        PIMAGE_NT_HEADERS GetNTheaderPt();

        //得到数据目录地址  根据INDEX

        PIMAGE_DATA_DIRECTORY GetDataDirectoryEnty(int index);

        //得到第一个节的指针

        PIMAGE_SECTION_HEADER GetFirstSectionHeader();

        //地址转换

        LPVOID RvaToOffet(LPVOID Rva);

        //得到可选头指针

        PIMAGE_OPTIONAL_HEADER32 GetOptionHeader();

        //得到文件头指针

        PIMAGE_FILE_HEADER GetFileHeader();

};

工具函数   
/*********************************************************************/

/*

此函数最先调用 保存文件句柄,内存文件地址

*/

bool PeTool::LoadFile(LPSTR lpFileName)

{

        HANDLE hFile;

    HANDLE hMapping;

    LPVOID ImageBase;

        memset(&m_filestruct,0,sizeof(MAP_FILE_STRUCT));

    hFile=CreateFile(lpFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, 

        FILE_ATTRIBUTE_NORMAL,0);

    if (!hFile)                                   

                return false;

    hMapping=CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);

    if(!hMapping)

    {                                                                        

        CloseHandle(hFile);

        return FALSE;

    }

    ImageBase=MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0);

    if(!ImageBase)

        {

                  CloseHandle(hFile);

                  CloseHandle(hMapping);

                  return false;

        }

        m_filestruct.hFile=hFile;

    m_filestruct.hMapping=hMapping;

    m_filestruct.ImageBase=ImageBase;

  }

/*********************************************************************/

/*

        把pe内存文件地址转换成dos  header pointer

*/

PIMAGE_DOS_HEADER PeTool::GetDosHeaderPt()

{

    PIMAGE_DOS_HEADER  pDosHeader;

        pDosHeader=(PIMAGE_DOS_HEADER)m_filestruct.ImageBase;

    return pDosHeader;

}

/*********************************************************************/

/*

  根据 dos header 中的e_lfanew字段 求出文件的NT headers 的指针

*/

PIMAGE_NT_HEADERS PeTool::GetNTheaderPt()

{

    PIMAGE_DOS_HEADER  pDosHeader=NULL;

        pDosHeader=GetDosHeaderPt();

    PIMAGE_NT_HEADERS  pNtHeader=NULL;

        pNtHeader=(PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew+(DWORD)m_filestruct.ImageBase);

    return pNtHeader;

}

/*********************************************************************/

/*

        根据NT header FileHeader 字段得出PIMAGE_FILE_HEADER

*/

PIMAGE_FILE_HEADER PeTool::GetFileHeader()

{

    PIMAGE_NT_HEADERS pNTHeader;

    PIMAGE_FILE_HEADER pFileHeader;

    pNTHeader=GetNTheaderPt();

    pFileHeader=&(pNTHeader->FileHeader);

    return pFileHeader;

}

/*********************************************************************/

/*

         根据RVA 求出文件地址

*/

LPVOID PeTool::RvaToOffet(LPVOID Rva)

{

    PIMAGE_FILE_HEADER pFileHeader;

        // get file pointer of fileheader

    pFileHeader=GetFileHeader();

    int NumberOfSections=0;

    NumberOfSections=pFileHeader->NumberOfSections;

    PIMAGE_SECTION_HEADER pSectionHeader;

        // get the first section pointer

    pSectionHeader=GetFirstSectionHeader();

    for(int i=0;i<NumberOfSections;i++)

    {

        int virtualsize;

        DWORD VirtualAddress;

        virtualsize=pSectionHeader->Misc.VirtualSize;

        VirtualAddress=pSectionHeader->VirtualAddress;

        if((DWORD)Rva>=VirtualAddress&&(DWORD)Rva<(VirtualAddress+virtualsize))

        {

                        return LPVOID((DWORD)Rva-VirtualAddress+pSectionHeader->PointerToRawData+(DWORD)m_filestruct.ImageBase);

        }

        pSectionHeader++;

    }

    return 0;

}

/*********************************************************************/

/*

根据索引值求的相应数据的数据目录表

*/

PIMAGE_DATA_DIRECTORY PeTool::GetDataDirectoryEnty(int index)

{

    PIMAGE_OPTIONAL_HEADER pOptionHeader;

        pOptionHeader=GetOptionHeader();

    return &(pOptionHeader->DataDirectory[index]);

}

/*********************************************************************/

/*

 得到第一个节表的文件地址

*/

PIMAGE_SECTION_HEADER  PeTool::GetFirstSectionHeader()

{

    PIMAGE_NT_HEADERS     pNtH=NULL;

    PIMAGE_SECTION_HEADER pSH=NULL;

    pNtH=GetNTheaderPt();

    pSH=IMAGE_FIRST_SECTION(pNtH);

    return  pSH;

}

/*********************************************************************/

/*

        得到OptionHeader 的文件地址

*/

PIMAGE_OPTIONAL_HEADER32 PeTool::GetOptionHeader()

{

    PIMAGE_NT_HEADERS pNTHeader;

    PIMAGE_OPTIONAL_HEADER pOptionalHeader;

        pNTHeader=GetNTheaderPt();

    pOptionalHeader=&(pNTHeader->OptionalHeader);

    return pOptionalHeader;

}

导入表
导入表的显示,我用列表框实现,那怎么组织数据呢,下面我用VECTOR来实现。结构如下

/*********************************************************************/

//此结构描述每一个DLL的信息

typedef struct _Import_table

{

        char Dllname[20];

        char OriginalFirstThunk[20];

        char TimeDateStamp[20];

        char ForwardChain[20];

        char name[20];

        char FirtThunk[20];

        vector<IMPORT_FUNC> m_funcsvector;

}IMPORT_TABLE;

/*********************************************************************/

//此结构描述每一个导入函数

typedef struct _Import_Func_Des

{

        char ThunkRva[20];

        char ThunkOffset[20];

        char ThunkValue[20];

        char Hin[20];

        char ApiName[100];

}IMPORT_FUNC;

/*********************************************************************/

//此结构用来描述每一个导入的dll,以及每一个导入函数,然后存入下面的vector中

vector<IMPORT_TABLE> m_ImportTableVector;

/*********************************************************************/

//分析Import_table

        void ImportTableAnalysis();

具体代码如下:

void PeTool::ImportTableAnalysis()

{

        //得到导入表RVA

        PIMAGE_DATA_DIRECTORY pImportRva=GetDataDirectoryEnty(1);

        //没有则返回

        if(pImportRva->VirtualAddress==0)

                return ;

        //根据RVA得到文件偏移

        PIMAGE_IMPORT_DESCRIPTOR

 pImportOffet=(PIMAGE_IMPORT_DESCRIPTOR)RvaToOffet((LPVOID)pImportRva->VirtualAddress);

        int i=0;

        //描述函数结构体

        IMPORT_FUNC funs;

        //描述dll的结构体

        IMPORT_TABLE importTable;

        //循环遍历每一个DLL

        while(pImportOffet[i].FirstThunk)

        {

                PIMAGE_IMPORT_DESCRIPTOR tempImport;

                tempImport=pImportOffet+i;

                char *pName=(char*)RvaToOffet((LPVOID)tempImport->Name);

                //初始化IMPORT_TABLE结构体 

            sprintf(importTable.Dllname,"%s",pName);

        sprintf(importTable.OriginalFirstThunk,"%0.8x",tempImport->OriginalFirstThunk);

                sprintf(importTable.TimeDateStamp,"%0.8x",tempImport->TimeDateStamp);

                sprintf(importTable.ForwardChain,"%0.8x",tempImport->ForwarderChain);

                sprintf(importTable.name,"%0.8x",tempImport->Name);

                sprintf(importTable.FirtThunk,"%0.8x",tempImport->FirstThunk);

                i++;

                //得到INT OFFSET

                PIMAGE_THUNK_DATA pInt=(PIMAGE_THUNK_DATA)RvaToOffet((LPVOID)tempImport->FirstThunk);

                PIMAGE_IMPORT_BY_NAME pbyName;

                int j=0;

                //循环遍历每一个函数,取得具体信息。

                while(pInt[j].u1.Function)

                {

                        sprintf(funs.ThunkRva,"%0.8x",tempImport->OriginalFirstThunk+j*4);

                        sprintf(funs.ThunkOffset,"%0.8x",(DWORD)pInt+j*4-(DWORD)m_filestruct.ImageBase);

                        sprintf(funs.ThunkValue,"%0.8x",pInt[j].u1.AddressOfData);

                        pbyName=(PIMAGE_IMPORT_BY_NAME)RvaToOffet((LPVOID)pInt[j].u1.AddressOfData);

                        //判断是否为序号导入的

                        if(pInt[j].u1.AddressOfData&0x80000000)

                        {

                                sprintf(funs.Hin,"%s","--");

                                sprintf(funs.ApiName,"ByOrdinal:%0.4x",pInt[j].u1.Function&0xFFFF);

                        }

                        else

                        {

                                sprintf(funs.Hin,"%0.4x",pbyName->Hint);

                                sprintf(funs.ApiName,"%s",(char*)pbyName->Name);

                        }

                        //把函数加入vector中

                        importTable.m_funcsvector.push_back(funs);

                        j++;

                }

                //把每一个DLL加入vector中

                m_ImportTableVector.push_back(importTable);        

                importTable.m_funcsvector.clear();

                m_isHaxImport=1;//是否有导入表标志位

        }

}

/******************************************************************

导出表
//导出表描述信息

/*********************************************************************/

typedef struct _Export_func_Descriptor

{

        char Ordinal[50];

        char Rva[50];

        char Offset[50];

        char name[200];

}EXPORT_FUNC_DESCRIPTOR;

用此结构存储导出函数的每一个信息

vector<EXPORT_FUNC_DESCRIPTOR> m_ExportFucDecVector;

/*********************************************************************/

下面是导出表的分析

void PeTool::ExportTableAnalysis()

{

        //得到数据目录的文件地址

        PIMAGE_DATA_DIRECTORY pData=GetDataDirectoryEnty(0);

        if(pData->VirtualAddress==0)

        {

                return ;

        }

        //得到导出表的文件地址

        PIMAGE_EXPORT_DIRECTORY pImexport=(PIMAGE_EXPORT_DIRECTORY )RvaToOffet((LPVOID)pData->VirtualAddress);

        m_exportOffset=(DWORD)pImexport;

        memcpy(&m_Image_Export,pImexport,sizeof(IMAGE_IMPORT_DESCRIPTOR));

        int sizeoffun=pImexport->NumberOfFunctions;

        int base=pImexport->Base;

        int sizeofname=pImexport->NumberOfNames;

        //得到导出函数文件地址

        DWORD* pFunc=(DWORD*)RvaToOffet((LPVOID)pImexport->AddressOfFunctions);

        //得到导出函数名字文件地址

        DWORD* pName=(DWORD*)RvaToOffet((LPVOID)pImexport->AddressOfNames);

        //得到ORdinal 文件地址

        const WORD*  pOrdinal=(WORD*)RvaToOffet((LPVOID)pImexport->AddressOfNameOrdinals);

        DWORD  Offset=0;

        EXPORT_FUNC_DESCRIPTOR  exportTable;

        for (int i = 0; i < sizeoffun; i++)

        {

                if(!pFunc[i])

                        continue;

                sprintf(exportTable.Ordinal,"%0.2x",base+i);

                sprintf( exportTable.Rva,"%0.8x",pFunc[i]);

                Offset=(DWORD)RvaToOffet((LPVOID)pFunc[i]);

                sprintf(exportTable.Offset,"%0.8x",Offset-(DWORD)m_filestruct.ImageBase);

  //是否按序号导出

                int index=isByname(pOrdinal,i,sizeofname);

                if(index==-1)

                {

                        sprintf(exportTable.name,"%s","ByOrdinal");

                }

                else

                {

                        char* name=(char*)RvaToOffet((LPVOID)pName[index]);

                        sprintf(exportTable.name,"%s",name);

                }

                m_ExportFucDecVector.push_back(exportTable);

        }

        m_isHasExport=1;//是否有导出表标志位

}

/*********************************************************************/

资源
资源这一块,由于显示是树形控件,则数据以树的结构组织,显示更为方便。问题关键是怎么组织成树的结构,我这里定义这个兄弟树结构体

/*********************************************************************/

//树节点

typedef struct _TreeNode

{

        char  data[256];//节点数据

        _TreeNode* pFirstChild;  //第一个子节点

        _TreeNode* pNextBrother;//兄弟节点

        _TreeNode()

        {

                pFirstChild=pNextBrother=NULL;

        }

/*********************************************************************/

/*

        把资源数据以树的结构组织

*/

void PeTool::CreateResourceTree()

{

        // 根节点

        m_pResourceHeadNode=new TREENODE;

        //资源地址

        PIMAGE_RESOURCE_DIRECTORY  pReDirectory=(PIMAGE_RESOURCE_DIRECTORY)GetResourceOffset();

        //资源地址,根节点为参数 添加个个节点

        AddNodeToResourceTree(m_pResourceHeadNode,pReDirectory,0);

}

/*********************************************************************/

/*

                参数:

                TREENODE*pNode,  父节点

                PIMAGE_RESOURCE_DIRECTORY pDirectory,  资源头

                int depath   深度

*/

void PeTool::AddNodeToResourceTree(TREENODE*pNode,PIMAGE_RESOURCE_DIRECTORY pDirectory,int depath )

{

        //得到  PIMAGE_RESOURCE_DIRECTORY_ENTRY  地址

        PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pDirectory+sizeof(IMAGE_RESOURCE_DIRECTORY));

        WORD    NumberOfNamedEntries;

    WORD    NumberOfIdEntries;

        NumberOfNamedEntries=pDirectory->NumberOfNamedEntries;

        NumberOfIdEntries=pDirectory->NumberOfIdEntries;

        //根据每一个资源目录有多少个来申请多少个节点

        _TreeNode* NodeArry=new _TreeNode[NumberOfNamedEntries+NumberOfIdEntries];

        pNode->pFirstChild=NodeArry;

        TREENODE* temp=NodeArry;

        for(int i=1;i<NumberOfNamedEntries+NumberOfIdEntries;i++)

        {

                temp->pNextBrother=NodeArry+i;

                temp=NodeArry+i;

        }

        temp=NodeArry;

        //采用先序遍历 来添加节点

        for (int i=0;i<NumberOfNamedEntries+NumberOfIdEntries;i++)

        {

                PIMAGE_RESOURCE_DIRECTORY_ENTRY  pEntryitem=pEntry+i;

                DWORD name=pEntryitem->Name;

                if(pEntryitem->DataIsDirectory)

                {

                        if(depath==0)//第一级目录 有id 标示和字符串标示

                        {

                        if(pEntryitem->NameIsString)//字符串标示

                        {

                                PIMAGE_RESOURCE_DIR_STRING_U pString=(PIMAGE_RESOURCE_DIR_STRING_U)RSAtoOffset(pEntryitem->NameOffset);

                                WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK |WC_DEFAULTCHAR ,

                                                                                        pString->NameString,pString->Length,

                                                                                        temp->data,

                                                                                        256,

                                                                                        NULL,

                                                                                        NULL);

                                temp->data[pString->Length]=0;

                        }

                        else //id标示

                        {

                                if(pEntryitem->Name>=17)

                                        sprintf(temp->data,"%s","自定义");

                                else

                                        strcpy(temp->data,szResourceType[pEntryitem->Name]);

                        }

                        }

                        else

                                sprintf(temp->data,"%d",pEntryitem->Id);

                        PIMAGE_RESOURCE_DIRECTORY dd=(PIMAGE_RESOURCE_DIRECTORY)RSAtoOffset(pEntryitem->OffsetToDirectory);

                        AddNodeToResourceTree(temp,dd,depath+1);

                }

                else

                {

                        PIMAGE_RESOURCE_DATA_ENTRY pDataEntry=(PIMAGE_RESOURCE_DATA_ENTRY)RSAtoOffset(pEntryitem->OffsetToData);

                        sprintf(temp->data,"OffsetToData (RVA): 0x%x,Size: %d,CodePage: %d,Reserved: %d",

                                pDataEntry->OffsetToData,pDataEntry->Size,pDataEntry->CodePage,pDataEntry->Reserved);

                }

                temp=temp->pNextBrother;

        }

}

/*********************************************************************/

上传的附件 PE_VIEW.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功