背景:
首先在这里要感谢“熊猫正正”和“玩命”两位前辈,是他们的文章让我能够写出此贴,在编写代码的时候正是对照着“熊猫正正”的国庆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直播授课
上传的附件: