以下代码是以控制台程序方式写的,主要是取出PE文件中的各节的基本信息、导入表信息和导出表信息,对于资源和重定位信息不具有提取能力。
注:这里说的静态PE文件是指通过内存映射这类形式加载到内存中的磁盘文件。
测试程序:
CFile cfile;
if(!cfile.Open("d://DATAVIEW.DLL", CFile::modeRead))
{
printf("open the file error!/n");
}
long lFileLen = cfile.GetLength();
unsigned char * pBuff = new unsigned char[lFileLen];
if(lFileLen != cfile.Read(pBuff, lFileLen))
{
printf("Read file error!/n");
}
cfile.Close();
PEClass pe(pBuff, lFileLen);
if(!pe.IsPEFile())
{
printf("The file is not a PE file!/n");
}
else
{
pe.ParseBaseData();
pe.ShowPESectionInfo();
pe.ShowPEImportInfo();
pe.ShowPEExportInfo();
}
delete []pBuff;
PE文件分析类程序:
class PEClass
{
public:
PEClass(unsigned char * ucpBuff, long lFileLen)
{
m_ucpFileBuffer = new unsigned char[lFileLen];
memcpy(m_ucpFileBuffer, ucpBuff, lFileLen);
m_lFileLen = lFileLen;
}
virtual ~PEClass()
{
if(m_ucpFileBuffer != NULL)
{
delete []m_ucpFileBuffer;
m_ucpFileBuffer = NULL;
}
}
//PE文件检测
bool IsPEFile()
{
//检测MZ标志
if(((IMAGE_DOS_HEADER*)m_ucpFileBuffer)->e_magic != IMAGE_DOS_SIGNATURE)
{
return false;
}
//检测PE标志
IMAGE_NT_HEADERS * pPEHeader = (IMAGE_NT_HEADERS *)((char *)m_ucpFileBuffer /
+ ((IMAGE_DOS_HEADER*)m_ucpFileBuffer)->e_lfanew);
if(pPEHeader->Signature != IMAGE_NT_SIGNATURE)
{
return false;
}
return true;
}
//解析PE文件中的主要数据
void ParseBaseData()
{
//获得PE文件头位置
m_pPEHeader = (IMAGE_NT_HEADERS *)(m_ucpFileBuffer + ((IMAGE_DOS_HEADER*)m_ucpFileBuffer)->e_lfanew);
//获得PE文件中含有节的数量
m_nSectionNo = m_pPEHeader->FileHeader.NumberOfSections;
//获得PE文件的PE头长度
m_nSizeOfPEHeader = sizeof IMAGE_NT_HEADERS;
//获得PE文件的节表位置
m_pPESection = (IMAGE_SECTION_HEADER *)((char *)m_pPEHeader + m_nSizeOfPEHeader);
}
//显示PE文件中节的基本信息
void ShowPESectionInfo();
//显示导入表的信息
void ShowPEImportInfo();
//显示导出表的信息
void ShowPEExportInfo();
private:
//将RVA转换成文件偏移
long GetOffsetOfRVA(long lRVA);
//获得RVA所在节的名称地址
void * GetSectionNameOfRVA(long lRVA);
private:
//PE文件在内存中的位置和大小
unsigned char * m_ucpFileBuffer;
long m_lFileLen;
//PE文件头的位置
IMAGE_NT_HEADERS * m_pPEHeader;
//PE文件头的大小
int m_nSizeOfPEHeader;
//节的数量
int m_nSectionNo;
//节的开始地址
IMAGE_SECTION_HEADER * m_pPESection;
};
//将RVA转换成文件偏移
long PEClass::GetOffsetOfRVA(long lRVA)
{
IMAGE_SECTION_HEADER * pPESection = m_pPESection;
//扫描每个节表
for(int i = 0; i < m_nSectionNo; i++, pPESection++)
{
//计算该节的RVA
long lSectionPos = pPESection->VirtualAddress;
//判断是否在该节里面
if(lRVA >= lSectionPos && lRVA < lSectionPos + pPESection->SizeOfRawData)
{
//在该节里面, 计算其对文件的偏移
return pPESection->PointerToRawData + (lRVA - lSectionPos);
}
}
//在节中不存在该RVA
return -1;
}
//获得RVA所在节的名称地址
void * PEClass::GetSectionNameOfRVA(long lRVA)
{
IMAGE_SECTION_HEADER * pPESection = m_pPESection
;
for(int i = 0; i < m_nSectionNo; i++, pPESection++)
{
//计算该节的RVA
long lSectionPos = pPESection->VirtualAddress;
//判断是否在该节里面
if(lRVA >= lSectionPos && lRVA < lSectionPos + pPESection->SizeOfRawData)
{
//在该节里面
return (void *)pPESection;
}
}
//在节中不存在该RVA
return NULL;
}
void PEClass::ShowPESectionInfo()
{
IMAGE_SECTION_HEADER * pPESection = m_pPESection;
printf("-----------------------------------------------------------------------------/n");
printf("节的名称 加载后节的地址 加载后节的大小 节在文件中的地址 节在文件中的大小/n");
printf("-----------------------------------------------------------------------------/n");
//扫描每个节
char *cpSectionName = new char[9];
for(int i = 0; i < m_nSectionNo; i++, pPESection++)
{
memset(cpSectionName, 0, 9);
//得到节的名称
char *p = (char *)pPESection->Name;
for(int i = 0; i < 8; i++, p++)
{
if(*p == '/0')
{
cpSectionName[i] = ' ';
}
else
{
cpSectionName[i] = *p;
}
}
printf("%-10s%-16X%-16X%-18X%-X/n", cpSectionName, pPESection->VirtualAddress, /
pPESection->Misc.VirtualSize, pPESection->PointerToRawData, /
pPESection->SizeOfRawData);
}
delete []cpSectionName;
printf("/n/n");
}
void PEClass::ShowPEImportInfo()
{
//获得导入表的RVA
long lImportRVA = m_pPEHeader->OptionalHeader.DataDirectory[8].VirtualAddress;
//判断该PE文件中是否含有导入表
if(lImportRVA == 0)
{
printf("the PE file does not contain the Import Table!/n");
printf("/n/n");
return ;
}
long lImportOffset = GetOffsetOfRVA(lImportRVA);
IMAGE_IMPORT_DESCRIPTOR * pImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR *)
((char *)m_ucpFileBuffer + lImportOffset);
for(; ; pImportDescriptor++)
{
//判断导入描述符是否结束
if(pImportDescriptor->OriginalFirstThunk == 0 && pImportDescriptor->TimeDateStamp == 0 /
&& pImportDescriptor->ForwarderChain == 0 && pImportDescriptor->Name == 0 && /
pImportDescriptor->FirstThunk == 0)
{
break;
}
//获得DLL文件的名称
long lDllName = pImportDescriptor->Name;
char *cpDllName = (char *)m_ucpFileBuffer + GetOffsetOfRVA(lDllName);
printf("This program import the function in %s/n", cpDllName);
long lThunkData = pImportDescriptor->OriginalFirstThunk;
if(lThunkData == 0)
{
lThunkData = pImportDescriptor->FirstThunk;
}
IMAGE_THUNK_DATA * lpThunkData = (IMAGE_THUNK_DATA *)((char *)m_ucpFileBuffer + GetOffsetOfRVA(lThunkData));
//显示信息
printf("--------------------------------------------------------------------/n");
printf("导入函数ID 导入函数名/n");
printf("--------------------------------------------------------------------/n");
for(long lImageThunkData = lpThunkData->u1.Ordinal; lImageThunkData != 0; lImageThunkData = (++lpThunkData)->u1.Ordinal)
{
long lSecrialFuncion;
unsigned char * cpFunName;
if(lImageThunkData & IMAGE_ORDINAL_FLAG32)
{
//该函数是以序号导入的
lSecrialFuncion = lImageThunkData & 0x7fffffff;
}
else
{
//该函数是以函数名导入的
IMAGE_IMPORT_BY_NAME importByName = *(IMAGE_IMPORT_BY_NAME*)((char*)m_ucpFileBuffer +/
GetOffsetOfRVA(lpThunkData->u1.Ordinal));
lSecrialFuncion = importByName.Hint;
cpFunName = &importByName.Name[0];
}
printf("%-12d%s/n", lSecrialFuncion, cpFunName);
}
printf("/n/n");
}
printf("/n/n");
}
void PEClass::ShowPEExportInfo()
{
//获得导出表的RVA
long lExportRVA = m_pPEHeader->OptionalHeader.DataDirectory[0].VirtualAddress;
//判断该PE文件中是否含有导出表
if(lExportRVA == 0)
{
printf("the PE file does not contain the Export Table!/n");
printf("/n/n");
return ;
}
long lExportOffset = GetOffsetOfRVA(lExportRVA);
IMAGE_EXPORT_DIRECTORY exportDirectory = *(IMAGE_EXPORT_DIRECTORY*)((char*)m_ucpFileBuffer + lExportOffset);
//解析基本信息
unsigned char *cpName = (unsigned char *)m_ucpFileBuffer + GetOffsetOfRVA(exportDirectory.Name);
int nBase = exportDirectory.Base;
int nFunCount = exportDirectory.NumberOfFunctions;
int nNameCount = exportDirectory.NumberOfNames;
long lAddOfFun = GetOffsetOfRVA(exportDirectory.AddressOfFunctions);
long lAddOfNames = GetOffsetOfRVA(exportDirectory.AddressOfNames);
long lAddOfNameOrdinals = GetOffsetOfRVA(exportDirectory.AddressOfNameOrdinals);
unsigned short * uspAddOfNameOrdinals = (unsigned short *)((unsigned char *)m_ucpFileBuffer + lAddOfNameOrdinals);
long * lpAddOfNames = (long *)((unsigned char *)m_ucpFileBuffer + lAddOfNames);
long * lpAddOfFun = (long*)((unsigned char *)m_ucpFileBuffer + lAddOfFun);
//显示信息
printf("the DLL/EXE name : %s/n", cpName);
printf("--------------------------------------------------------------------/n");
printf("导出函数ID 导出函数地址RVA 导出函数名/n");
printf("--------------------------------------------------------------------/n");
//按ID查找导出函数的入口地址, 有函数名的取出函数名
int nIndex = 0;
for(int nID = nBase; nID < nBase + nFunCount; nID++, nIndex++)
{
unsigned char * ucpFunName = NULL;
long lEnterAddRVA;
//扫描看是否有函数名
unsigned short * usp = uspAddOfNameOrdinals;
for(int i = 0; i < nNameCount; i++)
{
if(*usp == nIndex)
{
break;
}
usp++;
}
//判断是否找到该ID的函数名
if(i < nNameCount)
{
//找到
long lNameRVA = *(lpAddOfNames + nIndex);
ucpFunName = (unsigned char*)m_ucpFileBuffer + GetOffsetOfRVA(lNameRVA);
}
//获得导出函数的入口RVA
lEnterAddRVA = *(lpAddOfFun + nID);
//显示信息
if(ucpFunName != NULL)
{
printf("%-12d%-16ld%-s/n", nID, lEnterAddRVA, ucpFunName);
}
else
{
printf("%-12d%-16ld/n", nID, lEnterAddRVA);
}
}
printf("/n/n");
}