静态PE文件导入表、导出表分析程序

其实这个程序是在刚开始接触PE文件时写的了,当时只是想加深对PE文件的认识。也在去年放到这里来了,后来在整理页面时,被删了。今天把它再渡搬上来,只是想把它跟后面的几个文章形成一个系列。

    以下代码是以控制台程序方式写的,主要是取出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");
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值