首页
社区
课程
招聘
点滴记录--PE结构学习
发表于: 2012-7-8 18:49 16127

点滴记录--PE结构学习

2012-7-8 18:49
16127

背景:
首先在这里要感谢“熊猫正正”和“玩命”两位前辈,是他们的文章让我能够写出此贴,在编写代码的时候正是对照着“熊猫正正”的国庆PE总复习合集文章,一点点的敲出以下代码。
正文:
最近在学习PE结构,并试着写了些代码,尽管写PE结构用汇编写相当简洁、方便,可自己刚刚开始接触C++编程,为了更好的掌握熟悉C++编程特点就选择用了C++来写了,由于本人新手代码中多有混淆的地方,请大家多多体谅~,如果能提些建议那是感激不尽了!
当然论坛里,关于PE结构的理论知识和代码已经相当多了,这里仅仅是当做自己的学习笔记和历程记录下。更为以后掌握“壳”的知识铺路,本帖会不断更新完善、照着Stud_PE分析工具来写,最终会出一个界面版的分析工具,这里暂且先把关键部分的代码贴出、在处理各个节表、导入、导出表时并没有对数据部分直接输出结构,而是将数据存储到了结构当中便于之后读取和修改。如果需要输出可以对结构遍历输出。代码在VS2005和2008下编译通过
好了废话不说,上代码
首先是头文件中的结构如下:
#pragma once
/*注意在定义结构的时候用到了两种方式,一种是typedef结构定义方式,一种是struct 结构名方式;
之前一直用后者,后来发现有人用TypeDef方式;发现该方式在编译的时候
对于结构中用到的其他自定义结构不需要在他之前定义,而第二种方式则需要在调用之前定义(新手莫见笑!)
*/
//定义导入表模块结构
typedef struct _tagImportDllNode
{
        LPCSTR        pImprotDllName;                                //存储导入DLL名称
        struct _tagImportDllNode        *pBrother;                //DLL兄弟节点指向其他DLL模块结构
        struct _tagImportFuncNode        *pChild;                //DLL孩子节点指向DLL下的函数结构
}ImportDllNode;
//定义导入表模块中调用函数结构
typedef struct _tagImportFuncNode
{
        LPCSTR        pImportFuncName;                        //存储函数名称
        DWORD        ImportFuncAddr;                        //存储函数地址
        DWORD        ImportFuncInFileAddr;                        //存储函数地址在文件中的位置,便于以后(使用钩子时)修改地址
        struct _tagImportFuncNode *pBrother;                //兄弟节点指向其他函数结构
}ImportFuncNode;

//定义导出表模块结构
typedef struct _tagExportDllNode
{
        LPCSTR        pExprotDllName;                                //存储导出DLL名称
        struct _tagExportFuncNode        *pChild;                //DLL孩子节点指向DLL下的函数结构

}ExportDllNode;
//定义导出表模块中调用函数结构
typedef struct _tagExportFuncNode
{
        LPCSTR        pExportFuncName;                        //存储函数名称
        DWORD        ExportFuncAddr;                        //存储函数地址
        DWORD        ExportFuncInFileAddr;                        //存储函数地址在文件中的位置,便于以后(使用钩子时)修改地址
        DWORD        ExportFuncIndex;                        //存储函数按序号方式导出的索引       
        struct _tagExportFuncNode        *pBrother;                //兄弟节点指向其他函数结构
}ExportFuncNode;

//定义资源信息结构
struct ResourceInfo
{
        DWORD        ResourceAddr;                                //定义资源数据在文件中的地址
        DWORD        ResourceSize;                                //定义资源数据大小
};
//定义资源头结构
struct ResourceNode
{
        DWORD        ResourceNameOrID;                        //定义资源名称/ID
        LPCSTR        pResourceName;                                //如果是名称该变量为指向名称串的指针
        ResourceInfo        *pResource;                        //指向资源节点
        ResourceNode        *pBrother;                                //指向兄弟节点
        ResourceNode        *pChild;                                //指向孩子节点
};
//定义重定位表结构
struct RelocationNode
{
        DWORD                        FileAddr;                        //重定位项在文件中的偏移位置
        DWORD                        nNumber;                        //重定位块中需定位的项数
        LPCSTR                        lpRelocationAddr;        //指向重定位数据缓冲区
        RelocationNode        *pBrother;                                //指向其他重定位块
};
//定义节表结构
struct SectionNode
{
        BYTE                Name1[8];                                        //存储节表名称
        DWORD                VirualSize;                                //存储节表在内存中的大小
        DWORD                VirtualOffset;                        //存储节表在内存中的偏移
        DWORD                SizeOfRawData;                        //存储节表按节对齐后的大小
        DWORD                PointerToRawData;                //存储节表在文件中的偏移
        DWORD                Characteristics;                        //存储节表属性
        SectionNode *pBrother;                                //指向节表兄弟节点
};
//定义各个结构的头节点指针
ExportDllNode        *pExportTableHead;
ImportDllNode        *pImportTableHead;
ResourceNode        *pResourceTableHead;
RelocationNode        *pRelocationTableHead;
SectionNode                *pSectionTableHead;

//函数声明
bool GetImprotTableInfo(PVOID lpModuleAddr);
bool GetExportTableInfo(PVOID lpModuleAddr);
bool GetResourceTableInfo(PVOID lpModuleAddr);
bool GetRelocationTableInfo(PVOID lpModuleAddr);
bool GetSectionInfo(LPVOID lpModuleAddr);
LPVOID JudgePEFile(LPCSTR lpFileName);

是源文件如下:
//----------------------------PE文件判断---------------------------------------------
/*
HeadLine:        PE结构部分
Discription:判断文件是否为PE格式,如果是返回文件在内存中的地址,否则返回0
input:                文件名
output:                SUCCESS:返回模块地址;FAILE:返回0;
*/
LPVOID JudgePEFile(LPCSTR lpFileName)
{
        //打开文件
        OFSTRUCT ofs;
        HFILE hFile=::OpenFile(lpFileName, &ofs, OF_READ);
        if(HFILE_ERROR==hFile)
        {
                ::MessageBox(NULL,"Open file failed","Tip",0);
                return 0;
        }
        //创建文件映像
        HANDLE hMapFile=::CreateFileMapping((HANDLE)hFile,NULL,PAGE_READONLY,NULL,NULL,NULL);
        if(NULL==hMapFile)
        {
                ::MessageBox(NULL,"Create fileMapping failed","Tip",0);
                ::CloseHandle((HANDLE)hFile);
                return 0;
        }

        //将文件映射到内存空间并模块地址指向DOS结构
        LPVOID lpMapAddr=::MapViewOfFile(hMapFile, FILE_MAP_READ,NULL,NULL,0);
        IMAGE_DOS_HEADER *lpDosHead;
        lpDosHead=(IMAGE_DOS_HEADER*)lpMapAddr;

        //判断是否MZ标志
        if(lpDosHead->e_magic!='ZM')//IMAGE_DOS_SIGNATURE
        {
                ::CloseHandle((HANDLE)hFile);
                ::CloseHandle(hMapFile);
                ::UnmapViewOfFile(lpMapAddr);
                ::MessageBox(NULL,"DON'T BE A PE FILE","Tip",0);
                return 0;
        }

        //映射NT头
        IMAGE_NT_HEADERS *lpNTHead;
        lpNTHead=(IMAGE_NT_HEADERS*)((DWORD)lpDosHead+lpDosHead->e_lfanew);

        //是否PE标志
        if(lpNTHead->Signature!='EP')//IMAGE_NT_SIGNATURE
        {       
                ::CloseHandle((HANDLE)hFile);
                ::CloseHandle(hMapFile);
                ::UnmapViewOfFile(lpMapAddr);
                ::MessageBox(NULL,"DON'T BE A PE FILE","Tip",0);
                return 0;
        }
        return lpMapAddr;

}
//----------------------------PE文件导入表---------------------------------------------
/*
HeadLine:        获取输入表结构
Discription:根据模块地址获取输入表结构
input:                模块地址
output:                SUCCESS:返回1;FAILE:返回0;
*/
bool GetImprotTableInfo(PVOID lpModuleAddr)
{

        //由模块地址取PE头地址
        PIMAGE_NT_HEADERS pNTHead;
        pNTHead=::ImageNtHeader(lpModuleAddr);
        if(pNTHead!=NULL)
        {

                DWORD pImportDisp;
                pImportDisp=RvaToOffset(lpModuleAddr,pNTHead->OptionalHeader.DataDirectory[1].VirtualAddress);
                if (pImportDisp==NULL)
                {
                        return FALSE;
                }
                IMAGE_IMPORT_DESCRIPTOR *pIID;
                pIID=(IMAGE_IMPORT_DESCRIPTOR*)((DWORD)lpModuleAddr+pImportDisp);

                //取导入表函数名称
                DWORD* pImprotDllNameDisp,*pImprotDllNameAddr;
                pImprotDllNameDisp=(DWORD*)RvaToOffset(lpModuleAddr,pIID->Name);
                pImprotDllNameAddr=(DWORD*)((DWORD)lpModuleAddr+(DWORD)pImprotDllNameDisp);
                while(pImprotDllNameDisp!=0)
                {

                        //创建新节点将模块信息存入链表结构中
                        ImportDllNode* pNewDll=(ImportDllNode*)::LocalAlloc(LPTR,sizeof(ImportDllNode));
                        pNewDll->pImprotDllName=(char*)pImprotDllNameAddr;
                        ImportDllNode **pDllTemp;
                        pDllTemp=&pImportTableHead;
                        while(*pDllTemp!=0)
                        {
                                pDllTemp=&((*pDllTemp)->pBrother);
                        }
                        *pDllTemp=pNewDll;
                        //定位头结点
                        char *szDllName=(char*)pImprotDllNameAddr;
                        //printf("DLLName is %s\n",szDllName);
                        pDllTemp=&pImportTableHead;
                        while((*pDllTemp)->pImprotDllName!=szDllName)
                        {
                                pDllTemp=&((*pDllTemp)->pBrother);
                        }

                        IMAGE_THUNK_DATA *pITD;
                        pITD=(IMAGE_THUNK_DATA*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pIID->OriginalFirstThunk));
                        DWORD *pFunAddr=(DWORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pIID->FirstThunk));

                        IMAGE_IMPORT_BY_NAME *pIIBN;
                        ImportFuncNode **pFuncTemp;

                        //取函数名称
                        while(RvaToOffset(lpModuleAddr,pITD->u1.Function)!=NULL)
                        {

                                ImportFuncNode *pNewFunc=(ImportFuncNode*)::LocalAlloc(LPTR,sizeof(ImportFuncNode));
                                pIIBN=(IMAGE_IMPORT_BY_NAME*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pITD->u1.Function));
                                //函数名称方式时将函数名称添加到结构中
                                if(RvaToOffset(lpModuleAddr,pITD->u1.Function)>>31==0x0)
                                {       

                                        //创建新节点将函数信息存入链表结构中
                                        pNewFunc->pImportFuncName=(char*)pIIBN->Name;       
                                }
                                //输出函数名称,但注意并不是每个函数都以名称方式导出
                                //printf("FuncName is %s\n",(char*)pIIBN->Name);
                                pFuncTemp=&((*pDllTemp)->pChild);//pChild本身为结构指针,将他的地址传给变量所以这个变量需要时双层指针变量(映射地址)
                                while(*pFuncTemp!=0)
                                {
                                        pFuncTemp=&((*pFuncTemp)->pBrother);
                                }
                               
                                //无论序号还是名称方式导出函数地址不变
                                pNewFunc->ImportFuncAddr=*pFunAddr;
                               
                                //函数在文件中的地址
                                pNewFunc->ImportFuncInFileAddr=(DWORD)pFunAddr;
                               
                                //将新节点添加到结构中
                                *pFuncTemp=pNewFunc;
                               
                                //指向下一个IMAGE_THUNK_DATA
                                ++pITD;

                                //取下一个函数地址
                                ++pFunAddr;
                        }
                        ++pIID;
                        pImprotDllNameDisp=(DWORD*)RvaToOffset(lpModuleAddr,pIID->Name);
                        pImprotDllNameAddr=(DWORD*)((DWORD)lpModuleAddr+(DWORD)pImprotDllNameDisp);
                }
        }
        return 1;
}
//----------------------------PE文件导出表---------------------------------------------
/*
HeadLine:        获取输出表结构
Discription:根据模块地址获取输出表结构
input:                模块地址
output:                SUCCESS:返回1;FAILE:返回0;
*/
bool GetExportTableInfo(PVOID lpModuleAddr)
{
        //定义PE头结构
        PIMAGE_NT_HEADERS pNTHead;
        //取PE头地址
        pNTHead=::ImageNtHeader(lpModuleAddr);
        if(pNTHead!=NULL)
        {
                DWORD pExportDisp;
                //取导入表地址位于数据目录第一个项
                pExportDisp=RvaToOffset(lpModuleAddr,pNTHead->OptionalHeader.DataDirectory[0].VirtualAddress);
                if (pExportDisp==NULL)
                {
                        return FALSE;
                }
                IMAGE_EXPORT_DIRECTORY *pIED;
                pIED=(IMAGE_EXPORT_DIRECTORY*)((DWORD)lpModuleAddr+pExportDisp);
                DWORD* pExprotDllNameDisp,*pExprotDllNameAddr;
                pExprotDllNameDisp=(DWORD*)RvaToOffset(lpModuleAddr,pIED->Name);
                pExprotDllNameAddr=(DWORD*)((DWORD)lpModuleAddr+(DWORD)pExprotDllNameDisp);
                DWORD *pFunAddrDisp=(DWORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pIED->AddressOfFunctions));
                if(pExprotDllNameDisp!=0)
                {
                        //定义DLL名称指针--字节类型要按字符取
                        //创建新节点将模块信息存入链表结构中
                        ExportDllNode* pNewDll=(ExportDllNode*)::LocalAlloc(LPTR,sizeof(ExportDllNode));
                        pNewDll->pExprotDllName=(char*)pExprotDllNameAddr;
                        pExportTableHead=pNewDll;
                        ExportFuncNode **pFuncTemp;
                        DWORD nExportFuncNum;
                        DWORD nIndexBase;
                        nExportFuncNum=pIED->NumberOfFunctions;
                        nIndexBase=pIED->Base;

                        //取函数名称
                        DWORD *pFunAddr;
                        WORD *pFuncIndexDisp;
                        pFuncIndexDisp=(WORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pIED->AddressOfNameOrdinals));
                        for(int i=0; i<nExportFuncNum; i++)
                        {
                               
                                //将函数地址存入结构
                                ExportFuncNode *pNewFunc=(ExportFuncNode*)::LocalAlloc(LPTR,sizeof(ExportFuncNode));
                                pFunAddr=(DWORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,*pFunAddrDisp));//*pFunAddrDisp;
                                pNewFunc->ExportFuncAddr=*pFunAddr;//*(DWORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,*pFunAddr));//*pFuncAddrDisp;//(DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pFuncAddrDisp);
                                pNewFunc->ExportFuncInFileAddr=(DWORD)pFunAddr;
                                //将函数序号存入结构;从1开始
                               
                                pNewFunc->ExportFuncIndex=(WORD)(*pFuncIndexDisp)+nIndexBase;

                                //将函数名称存入(注意无名称方式)
                                DWORD *NameAddrDisp;
                                NameAddrDisp=(DWORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pIED->AddressOfNames));
                                pNewFunc->pExportFuncName=(LPCSTR)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,*NameAddrDisp));
                                //printf("FuncName is %s\n",(char*)pIIBN->Name);
                                pFuncTemp=&(pExportTableHead->pChild);//pChild本身为结构指针,将他的地址传给变量所以这个变量需要时双层指针变量(映射地址)
                                while(*pFuncTemp!=0)
                                {
                                        pFuncTemp=&((*pFuncTemp)->pBrother);
                                }

                                //将新节点添加到结构中
                                *pFuncTemp=pNewFunc;
                                //去下一个函数地址
                                pFunAddrDisp++;
                                pFuncIndexDisp++;
                        }
                }
        }
        return TRUE;
}
//----------------------------PE文件资源表---------------------------------------------
/*
HeadLine:        获取资源表结构
Discription:根据模块地址获取资源表结构
input:                模块地址
output:                SUCCESS:返回1;FAILE:返回0;
*/
bool GetResourceTableInfo(PVOID lpModuleAddr)
{
        //定义PE文件头结构
        PIMAGE_NT_HEADERS pNTHead;
        //取PE文件头地址
        pNTHead=::ImageNtHeader(lpModuleAddr);
        if(pNTHead!=NULL)
        {
                //定位资源表位置
                DWORD pResourceDisp,*pResourceAddr;
                pResourceDisp=RvaToOffset(lpModuleAddr,pNTHead->OptionalHeader.DataDirectory[2].VirtualAddress);
                if (pResourceDisp==NULL)
                {
                        return FALSE;
                }
                IMAGE_RESOURCE_DIRECTORY *IRD;
                pResourceAddr=(DWORD*)((DWORD)lpModuleAddr+pResourceDisp);
                IRD=(IMAGE_RESOURCE_DIRECTORY*)pResourceAddr;
                //保存一级目录下子目录个数
                ResourceNode **TempResourceNodeFirst;
                ResourceNode* pResourceNodeNew=(ResourceNode*)::LocalAlloc(LPTR,sizeof(ResourceNode));
                pResourceTableHead=pResourceNodeNew;
                //定义
                DWORD nIRDSize,nChildNum;
                nIRDSize=sizeof(IMAGE_RESOURCE_DIRECTORY);
                nChildNum=IRD->NumberOfNamedEntries+IRD->NumberOfIdEntries;
                TempResourceNodeFirst=&(pResourceTableHead->pChild);
                //循环遍历子目录
                for (int i=0; i<nChildNum; i++)
                {
                        //新建二级目录节点
                        ResourceNode* pResourceNodeNew=(ResourceNode*)::LocalAlloc(LPTR,sizeof(ResourceNode));
                        //定位目录数据项
                        IMAGE_RESOURCE_DIRECTORY_ENTRY *IRDE;
                        DWORD dIRDEDisp;
                        dIRDEDisp=(DWORD)IRD+nIRDSize;
                        IRDE=(IMAGE_RESOURCE_DIRECTORY_ENTRY *)dIRDEDisp;

                        //资源名称或ID,最高位为0表示资源;ID为1时,低31位表示名称指针
                        if((IRDE->Name)>>31==0x0)
                        {
                                //保存资源ID       
                                pResourceNodeNew->ResourceNameOrID=IRDE->Name;
                        }
                        else{
                                pResourceNodeNew->pResourceName=(LPCSTR)((IRDE->Name)&0X7FFFFFFF);
                        }     

                        if (*TempResourceNodeFirst!=0)
                        {
                                TempResourceNodeFirst=&((*TempResourceNodeFirst)->pBrother);
                        }
                        *TempResourceNodeFirst=pResourceNodeNew;
                        //下一级目录或者数据页指针,最高位为0表示指向IMAGE_RESOURCE_DATA_ENTRY结构;ID为1时,低31位表示指向IMAGE_RESOURCE_DIRECTORY结构
                        if((IRDE->OffsetToData)>>31==0x0)
                        {

                        }
                        else{
                                DWORD dTempIRD,nChildNum;
                                IMAGE_RESOURCE_DIRECTORY* IRD;
                                ResourceNode **TempResourceNodeSecond;
                                dTempIRD=(DWORD)pResourceAddr+IRDE->OffsetToData&0x7FFFFFFF;
                                IRD=(IMAGE_RESOURCE_DIRECTORY*)dTempIRD;
                                nChildNum=IRD->NumberOfIdEntries+IRD->NumberOfNamedEntries;
                                TempResourceNodeSecond=&((*TempResourceNodeFirst)->pChild);
                                DWORD nIRDSize=sizeof(IMAGE_RESOURCE_DIRECTORY);
                                for (int i=0; i<nChildNum; i++)
                                {
                                        ResourceNode *pResourceNodeNew=(ResourceNode *)::LocalAlloc(LPTR,sizeof(ResourceNode));
                                        IMAGE_RESOURCE_DIRECTORY_ENTRY *IRDE;
                                        IRDE=(IMAGE_RESOURCE_DIRECTORY_ENTRY *)((DWORD)IRD+nIRDSize);
                                        if((IRDE->Name)>>31==0x0)
                                        {
                                                //保存资源ID
                                                pResourceNodeNew->ResourceNameOrID=IRDE->Name;
                                        }
                                        else{
                                                IMAGE_RESOURCE_DIR_STRING_U *IRDSU;
                                                IRDSU=(IMAGE_RESOURCE_DIR_STRING_U*)((IRDE->Name)&0X7FFFFFFF);
                                                pResourceNodeNew->pResourceName=(LPCSTR)((IRDE->Name)&0X7FFFFFFF);
                                        }      
                                        if (*TempResourceNodeSecond!=0)
                                        {
                                                TempResourceNodeSecond=&((*TempResourceNodeSecond)->pBrother);
                                        }
                                        *TempResourceNodeSecond=pResourceNodeNew;
                                        if (IRDE->OffsetToData>>31==0x0)
                                        {
                                               
                                        }
                                        else{
                                                DWORD dTempIRD,nChildNum;
                                                IMAGE_RESOURCE_DIRECTORY* IRD;
                                                ResourceNode **TempResourceNodeThird;
                                                dTempIRD=(DWORD)((DWORD)pResourceAddr+IRDE->OffsetToData&0x7FFFFFFF);
                                                IRD=(IMAGE_RESOURCE_DIRECTORY*)dTempIRD;

                                                nChildNum=IRD->NumberOfIdEntries+IRD->NumberOfNamedEntries;
                                                TempResourceNodeThird=&((*TempResourceNodeSecond)->pChild);
                                                DWORD nIRDSize=sizeof(IMAGE_RESOURCE_DIRECTORY);
                                                for (int i=0; i<nChildNum; i++)
                                                {
                                                        ResourceNode *pResourceNodeNew=(ResourceNode *)::LocalAlloc(LPTR,sizeof(ResourceNode));
                                                        IMAGE_RESOURCE_DIRECTORY_ENTRY *IRDE;
                                                        IRDE=(IMAGE_RESOURCE_DIRECTORY_ENTRY *)((DWORD)IRD+nIRDSize);
                                                        if((IRDE->Name)>>31==0x0)
                                                        {
                                                                //保存资源ID
                                                                pResourceNodeNew->ResourceNameOrID=IRDE->Name;
                                                        }
                                                        else{
                                                                IMAGE_RESOURCE_DIR_STRING_U *IRDSU;
                                                                IRDSU=(IMAGE_RESOURCE_DIR_STRING_U*)((IRDE->Name)&0X7FFFFFFF);
                                                                pResourceNodeNew->pResourceName=(LPCSTR)((IRDE->Name)&0X7FFFFFFF);
                                                        }      
                                                        if (*TempResourceNodeThird!=0)
                                                        {
                                                                TempResourceNodeThird=&((*TempResourceNodeThird)->pBrother);
                                                        }
                                                        *TempResourceNodeThird=pResourceNodeNew;
                                                        //存储资源位置和大小
                                                        ResourceInfo *pResourceInfoNew=(ResourceInfo *)LocalAlloc(LPTR,sizeof(ResourceInfo));
                                                        dIRDEDisp=(DWORD)pResourceAddr+IRDE->OffsetToData;
                                                        if (IRDE->OffsetToData>>31==0x0)
                                                        {
                                                                //第三层目录结构下是一个数据页
                                                                IMAGE_RESOURCE_DATA_ENTRY *IRDE;
                                                                IRDE=(IMAGE_RESOURCE_DATA_ENTRY *)dIRDEDisp;
                                                                //资源大小
                                                                pResourceInfoNew->ResourceSize=IRDE->Size;
                                                                //资源数据地址
                                                                pResourceInfoNew->ResourceAddr=(DWORD)lpModuleAddr+IRDE->OffsetToData;
                                                        }
                                                        (*TempResourceNodeThird)->pResource=pResourceInfoNew;
                                                        nIRDSize=nIRDSize+sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
                                                }
                                               
                                        }
                                        nIRDSize=nIRDSize+sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
                                }
                        }
                        nIRDSize+=sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
                }
        }
        return TRUE;
}
//----------------------------PE文件重定位表---------------------------------------------
/*
HeadLine:        获取重定位结构
Discription:根据模块地址获取重定位表结构
input:                模块地址
output:                SUCCESS:返回1;FAILE:返回0;
*/
bool GetRelocationTableInfo(LPVOID lpModuleAddr)
{
        //定义PE头结构
        PIMAGE_NT_HEADERS pNTHead;
        //取PE头地址
        pNTHead=::ImageNtHeader(lpModuleAddr);
        if(pNTHead!=NULL)
        {
                DWORD pRelocationDisp;
                //取重定位表地址位于第五个目录表
                pRelocationDisp=RvaToOffset(lpModuleAddr,pNTHead->OptionalHeader.DataDirectory[5].VirtualAddress);
                if (pRelocationDisp==NULL)
                {
                        return FALSE;
                }
                RelocationNode **TempRelocationTableHead;
                TempRelocationTableHead=&pRelocationTableHead;
                //定义重定位结构指针
                IMAGE_BASE_RELOCATION *pIBR;
                pIBR=(IMAGE_BASE_RELOCATION *)((DWORD)lpModuleAddr+pRelocationDisp);
                //循环查找重定位表
                while (pIBR->VirtualAddress!=NULL)
                {
                        //定义新节点
                        RelocationNode *pRelocationNodeNew=(RelocationNode*)LocalAlloc(LPTR,sizeof(RelocationNode));
                        //取偏移地址
                        pRelocationNodeNew->FileAddr=RvaToOffset(lpModuleAddr,pIBR->VirtualAddress);
                        //取重定位块中需定位的个数
                        DWORD nRelocationNum=(pIBR->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/2;
                        pRelocationNodeNew->nNumber=nRelocationNum;
                        //为存储重定位偏移地址分配空间
                        LPCSTR pRelocationAddr=(LPCSTR)LocalAlloc(LPTR,nRelocationNum*2);
                        strncpy((char*)pRelocationAddr,(char*)((DWORD)pIBR+sizeof(IMAGE_BASE_RELOCATION)),nRelocationNum*2);
                        pRelocationNodeNew->lpRelocationAddr=pRelocationAddr;
                        //查找空节点插入新节点
                        while (*TempRelocationTableHead!=0)
                        {
                                TempRelocationTableHead=&((*TempRelocationTableHead)->pBrother);
                        }
                        *TempRelocationTableHead=pRelocationNodeNew;
                        //移动指针指向下一个节表
                        pIBR=(IMAGE_BASE_RELOCATION*)((DWORD)pIBR+pIBR->SizeOfBlock);       
                }
               
        }
        return TRUE;
}
//----------------------------PE文件节表---------------------------------------------
/*
HeadLine:        取节表信息
input:                模块地址
output:                SUCCESS:返回1;FAILE:返回0;
*/
bool GetSectionInfo(LPVOID lpModuleAddr)
{
        //定义PE头结构
        PIMAGE_NT_HEADERS pNTHead;
        //取PE头地址
        pNTHead=::ImageNtHeader(lpModuleAddr);
        if(pNTHead!=NULL)
        {
                //取节表地址
                IMAGE_SECTION_HEADER *pISH;
                pISH=(IMAGE_SECTION_HEADER*)((DWORD)pNTHead+sizeof(IMAGE_NT_HEADERS));
                if (pISH->VirtualAddress==NULL)
                {
                        return FALSE;
                }
                //定义存储节表结构的二级指针方便定位插入节点
                SectionNode **TempSectionTableHead;
                TempSectionTableHead=&pSectionTableHead;
                //取节表数量
                int nSectionNum;
                nSectionNum=pNTHead->FileHeader.NumberOfSections;
                //取节表结构内容并保存
                for (int i=0; i<nSectionNum; i++)
                {
                        SectionNode *pSectionNodeNew=(SectionNode*)LocalAlloc(LPTR,sizeof(SectionNode));
                        pSectionNodeNew->VirtualOffset=pISH->VirtualAddress;
                        pSectionNodeNew->Characteristics=pISH->Characteristics;
                        pSectionNodeNew->VirualSize=pISH->Misc.PhysicalAddress;
                        pSectionNodeNew->PointerToRawData=pISH->PointerToRawData;
                        pSectionNodeNew->SizeOfRawData=pISH->SizeOfRawData;
                        char *pSectionNameDest=(char*)(pSectionNodeNew->Name1);
                        char *pSectionNameSour=(char*)(pISH->Name);
                        strncpy(pSectionNameDest,pSectionNameSour,8);
                        //查找空节点插入新节点
                        while (*TempSectionTableHead!=0)
                        {
                                TempSectionTableHead=&((*TempSectionTableHead)->pBrother);
                        }
                        *TempSectionTableHead=pSectionNodeNew;
                        //移动指针指向下一个节表
                        pISH=(IMAGE_SECTION_HEADER*)((DWORD)pISH+sizeof(IMAGE_SECTION_HEADER));       
                }

        }
        return TRUE;
}
//----------------------------PE文件地址转换---------------------------------------------
/*
HeadLine:        内存偏移值Rva值转换成文件的偏移值
input:                模块地址,内存偏移Rva
output:                相对于文件的偏移值
*/
BOOL RvaToOffset(LPVOID lpMoudle,DWORD Rva)
{

        //定义变量存储转换后的偏移值和节表数
        DWORD FileOffset;
        WORD nSectionNum;

        //取NT结构头
        IMAGE_NT_HEADERS        *pNTHead;
        pNTHead=ImageNtHeader(lpMoudle);
        nSectionNum=pNTHead->FileHeader.NumberOfSections;

        //取节表结构头
        IMAGE_SECTION_HEADER *pSectionHead;
        pSectionHead=(IMAGE_SECTION_HEADER *)((DWORD)pNTHead+sizeof(IMAGE_NT_HEADERS));
       
        //循环比较Rva值所对应节表的偏移
        for(int i=0; i<nSectionNum; i++)
        {
                if((pSectionHead->VirtualAddress<=Rva) && (Rva<(pSectionHead->SizeOfRawData+pSectionHead->VirtualAddress)))
                {
                        FileOffset=Rva-pSectionHead->VirtualAddress+pSectionHead->PointerToRawData;
                        return FileOffset;
                }
                pSectionHead=(IMAGE_SECTION_HEADER *)((DWORD)pSectionHead+sizeof(IMAGE_SECTION_HEADER));
        }
        return FALSE;
}
注意,最初写导入表时没有写地址转换即Rva地址转换成文件中的偏移,对于节(SectionAlignment)和文件(FileAlignment)对齐值相同的没有问题,对于对齐值不同的可就不行了。因为当文件被映射到内存后内存中的映像是和文件中各个数据位置相同的,通俗的说就是将文件整个照搬到内存中,并不是按照内存块映射各个部分(这种情况是在程序执行装入内存后才会发生的)。
欢迎大家指正谢谢~


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

上传的附件:
收藏
免费 6
支持
分享
最新回复 (18)
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
PE结构算是蛮复杂的
想必LZ也花了不少精力...
2012-7-8 21:33
0
雪    币: 269
活跃值: (906)
能力值: ( LV12,RANK:345 )
在线值:
发帖
回帖
粉丝
3
有两周吧,平日上班,业余时间写的
2012-7-8 22:41
0
雪    币: 51
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
有必要写个RvaToOffset函数吗???
LPVOID LoadPe()
{
	char* p ="c:\\ollydbg.exe";
	DWORD dwFileSize;
	HANDLE hFile;
	HANDLE hMap;
	LPVOID pMapAddress;
	LPVOID pImageAddress;
	PIMAGE_DOS_HEADER pDosHeader;							
	PIMAGE_NT_HEADERS pNtHeader;
	PIMAGE_SECTION_HEADER pSectionHeader;
	DWORD dwPeMemorySize;

	hFile = CreateFile(p,
		GENERIC_ALL,
		FILE_SHARE_READ,
		NULL,
		OPEN_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	hMap = CreateFileMapping(hFile,
		NULL,
		PAGE_READONLY,
		0,
		0,
		0);
	pMapAddress = MapViewOfFile(hMap,
		FILE_MAP_READ,
		0,
		0,
		0);

	dwFileSize = GetFileSize(hFile,NULL);

	pDosHeader = (PIMAGE_DOS_HEADER)pMapAddress;
	
	pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pMapAddress + pDosHeader->e_lfanew);
	
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeader + 4 + 
		sizeof(IMAGE_FILE_HEADER)+pNtHeader->FileHeader.SizeOfOptionalHeader);

	dwPeMemorySize = pNtHeader->OptionalHeader.SizeOfImage;

	pImageAddress = VirtualAlloc(
								NULL,
								dwPeMemorySize,
								MEM_COMMIT,
								PAGE_READWRITE);

	memcpy(pImageAddress,(char*)pMapAddress,pNtHeader->OptionalHeader.SizeOfHeaders);
	
	for (int i = 0;i<pNtHeader->FileHeader.NumberOfSections;i++)
	{
		//按内存结构拷贝到内存
		memcpy((char*)pImageAddress+pSectionHeader[i].VirtualAddress,
			(char*)pDosHeader+pSectionHeader[i].PointerToRawData,
			pSectionHeader[i].SizeOfRawData);	
	}

	CloseHandle(hFile);
	CloseHandle(hMap);
	UnmapViewOfFile(pMapAddress);
	return pImageAddress;
}
2012-7-9 16:28
0
雪    币: 78
活跃值: (85)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
当然有必要,试想如果你要通过Rva修改原始PE呢?
还有你这个代码里有很多隐含的BUG哦,亲~~~
2012-7-9 17:53
0
雪    币: 15
活跃值: (76)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢楼主,学习。。。
2012-7-10 09:30
0
雪    币: 411
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
又见PE好文,偶最近也在看这个东西,谢谢了
2012-7-10 13:36
0
雪    币: 524
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
我也一直在疑惑:pe文件映射到进程地址空间里面是把磁盘pe文件照搬呢,还是按照内存对齐粒度对齐呢。
2013-2-20 16:49
0
雪    币: 269
活跃值: (906)
能力值: ( LV12,RANK:345 )
在线值:
发帖
回帖
粉丝
9
当然是后者了,看看文件映射部分
2013-2-20 17:15
0
雪    币: 524
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
楼主写这篇文章最后的意思是说在映射到进程地址空间的时候是按照磁盘文件原样照搬,然后装入的时候才是按照内存块对齐的
2013-2-20 20:57
0
雪    币: 269
活跃值: (906)
能力值: ( LV12,RANK:345 )
在线值:
发帖
回帖
粉丝
11

大致就是上边那样映射,内存中可能还会插入些资源类的数据
上传的附件:
2013-2-20 21:56
0
雪    币: 524
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
注意,最初写导入表时没有写地址转换即Rva地址转换成文件中的偏移,对于节(SectionAlignment)和文件(FileAlignment)对齐值相同的没有问题,对于对齐值不同的可就不行了。因为当文件被映射到内存后内存中的映像是和文件中各个数据位置相同的,通俗的说就是将文件整个照搬到内存中,并不是按照内存块映射各个部分(这种情况是在程序执行装入内存后才会发生的)。  这句话不就是说文件被映射进内存是是按照磁盘原样数据照搬的
2013-2-21 08:18
0
雪    币: 269
活跃值: (906)
能力值: ( LV12,RANK:345 )
在线值:
发帖
回帖
粉丝
13
拜托,你上边都说了当对齐值相同时,照搬到内存,不对齐时能照搬吗
2013-2-21 09:50
0
雪    币: 524
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
上面说的是你帖子里的原话,把文件映射到进程地址空间的分布跟磁盘文件中的数据位置相同。
2013-2-21 10:46
0
雪    币: 269
活跃值: (906)
能力值: ( LV12,RANK:345 )
在线值:
发帖
回帖
粉丝
15
十分抱歉,时间久了忘记了,上面的话确实有歧义,现在无法编辑修改了。
2013-2-21 11:04
0
雪    币: 524
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
CreateFileMapping把pe文件映射进进程地址空间的时候各块是按照内存对齐粒度对齐的?
2013-2-21 11:15
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
嗯 挺经典的 LZ有心了
2013-2-21 15:50
0
雪    币: 184
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
做个记号,有时间看
2013-3-7 15:04
0
雪    币: 159
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
19
最近也在研究PE结构,仔细拜读了LZ的帖子,有个疑问
在bool GetExportTableInfo(PVOID lpModuleAddr)函数中:
for循环中有这样极具代码:

//将函数名称存入(注意无名称方式)
DWORD *NameAddrDisp;
NameAddrDisp=(DWORD*)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,pIED->AddressOfNames));
pNewFunc->pExportFuncName=(LPCSTR)((DWORD)lpModuleAddr+RvaToOffset(lpModuleAddr,*NameAddrDisp));

这个样子的话,NameAddrDisp的值,每次循环中应该都是一样的,最终取出的导出函数的名称也应该是一样的吧,所以最终对于某一个特定DLL,其导出函数表链表的所有节点中函数名称这一项都会是base+1这个序号的名字吧。
是不是应该把NameAddrDisp的定义和赋值这两句拿到for循环外面,并且在for循环结尾处来个:
NameAddrDisp++;

如有说的不对的地方,还望指出。
2013-3-11 21:23
0
游客
登录 | 注册 方可回帖
返回
//