Auller 发表于 2016-10-6 21:12

自己总结的简单PE结构解析,有需要的可以看看

DOS标志
PIMAGE_DOS_HEADER PDos=(PIMAGE_DPS_HEADER)Buf(Buf为文件大小);
根据pDos->e_magic判断是否为pe结构(010 Editor中显示为4d5a)
判断NT头
PIMAGE_NT_HEADER32 pNtH=(PIMAGE_NT_HEADERS)(Buf + pDosH->e_lfanew);
根据pNtH->Signature判断是否为pe结构(010 Editor中显示为4550)
判断可选头
PIMAGE_OPTIONAL_HEADER32 pOptH=&(pNtH->OptionalHeader);
pOptH->AddressOfEntryPoint   PE入口点
pOptH->ImageBase             镜像基址
pOptH->SizeOfImage         镜像大小
pOptH->BaseOfCode            代码基址
pOptH->BaseOfData            数据基址
pOptH->SectionAlignment       块对齐
pOptH->FileAlignment          文件对齐
pOptH->Magic                  标志字
pOptH->Subsystem            子系统
pOptH->SizeOfHeaders         部首大小
pOptH->CheckSum            校验和
pOptH->NumberOfRvandSizes    RVA数及大小
pNtH->FileHeader.NumberOfSections   区段数目
pNtH->FileHeader.TimeDateStamp      日期时间标志
pNtH->FileHeader.Characteristics      特征值
pNtH->FileHeader.SizeOfOptionalHeader   可选头部大小

判断区段头
PIMAGE_SECTION_HEADER pSec= IMAGE_FIRST_SECTION(pNtH);
pSec->Name                  区段名字
pSec->VirtualAddress      区段起始的相对基址
pSec->Misc.VirtualSize       区段大小
pSec->PointerToRawDaata      区段的文件偏移
pSec->SizeOfRawData          区段在文件中的大小
pSec->Charavteristics      区段属性
.text   一般是代码段,这个是非常重要的
.data   一般是数据段
.bss    表示为初始化的数据,比如static变量,可能是在进入一个函数的时候才被初始化的
.rdata表示只读的数据,比如字符串
.texebss 和代码有关,不是很清楚做什么用的
.idata和.edata    储存导入表和导出表的信息
.rsrc      存储资源的区段
.relcoc    存储重定位信息的区段

获取数据目录
PIMAGE_DATA_DIRECTORY pDatD = (pOptH->DataDirectory);
目录表的内容是连续的,定义为:
pDatD.VirtualAddress;
pDatD.Size;
获取导出表(目录表第一个)
DWORD RvaOffset(DWORD Rva)
{
      PIMAGE_SECTION_HEADER pSECH = IMAGE_FIRST_SECTION(pNtH);
      DWORD Num = pNtH->FileHeader.NumberOfSections;
      if (Rva<pSECH.PointerToRawData)
      {
            return Rva;
      }
      for (DWORD i = 0; i < Num;i++)
      {
            if (
                  (Rva >= pSECH.VirtualAddress) &&
                  (Rva <= pSECH.VirtualAddress + pSECH.Misc.VirtualSize)//是否在当前项区段的位置,防止蹿去其他区段
                  )
            {
                  return (Rva - pSECH.VirtualAddress + pSECH.PointerToRawData);//相对虚拟地址-相对虚拟区段头地址+原文件区段地址=原文件区段偏移
            }
      }
      return TRUE;
}

PIMAGE_EXPORT_DIRECTORY pExpD=(PIMAGE_EXPORT_DIRECTORY)(Buf + RvaOffset(pDatD->VirtualAddress));
Buf + RvaOffset(pExpD->AddressOfFunctions);      //函数地址
Buf + RvaOffset(pExpD->AddressOfNames);      //函数名地址
Buf + RvaOffset(pExpD->AddressOfNameOrdinals);//函数序号地址
pExpD->NumberOfFunctions; //函数数量
pExpD->AddressOfNames;      //函数名称数量


获取导入表信息(目录第二个)
PIMAGE_IMPORT_DESCRIPTORpImpD=(PIMAGE_IMPORT_DESCRIPTOR)((long)Buf + RvaOffset(pDatD->VirtualAddress));
通过DLL名遍历
while (pImpD->Name)
{
                //DLL名称相关
      CString str((AllPe.Buf + AllPe.RvaOffset(pImpD->Name)));   //导入表名字      
      m_InList1.InsertItem(i, str);                                    
      str.Format(L"%08X", pImpD->OriginalFirstThunk);            //指向一个结构数组的相对虚拟基址,   
                                                                   //结构体数组叫输入名称表
   m_InList1.SetItemText(i, 1, str);      
      str.Format(L"%08X", pImpD->TimeDateStamp);               //文件建立时间
      m_InList1.SetItemText(i, 2, str);      
      str.Format(L"%08X", pImpD->ForwarderChain);                //转发机制用的到
      m_InList1.SetItemText(i, 3, str);               
      str.Format(L"%08X", pImpD->Name);                        //导入的PE文件的名字的相对虚拟RVA
      m_InList1.SetItemText(i, 4, str);      
      str.Format(L"%08X", pImpD->FirstThunk);                  //指向一个结构提数组的相对虚拟地址,
                                                               //结构体数组叫输入地址表
      m_InList1.SetItemText(i, 5, str);
      i++;
      pImpD++;
}

获取资源表
PIMAGE_RESOURCE_DIRECTORY pResD = (PIMAGE_RESOURCE_DIRECTORY)(Buf + RvaOffset(pDatD->VirtualAddress));
DWORD ReSize = pResD->NumberOfIdEntries + pResD->NumberOfNamedEntries;
      PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD + sizeof(IMAGE_RESOURCE_DIRECTORY));
      
资源表分为3层
第一层
名称结构、大小
pResDE->NameIsString;ReSize。
HTREEITEM hItem;
      HTREEITEM hSubItem;
      CString str;
      for (DWORD FirstOrder = 0; FirstOrder < ReSize; FirstOrder++)
      {
                //第一层 假如是字符串标识
                if (pResDE->NameIsString == 1)
                {
                        PIMAGE_RESOURCE_DIR_STRING_U pREsDStrU = (PIMAGE_RESOURCE_DIR_STRING_U)((long)pResD + pResDE->NameOffset);
                        hItem = m_ResTree.InsertItem(pREsDStrU->NameString, NULL, NULL);///root就是节点的标题
                }
                else
                {
                        switch (pResDE->Name)
                        {
                        case 0x1:
                              hItem = m_ResTree.InsertItem(L"鼠标指针", NULL, NULL);
                              break;
                        case 0x2:
                              hItem = m_ResTree.InsertItem(L"位图", NULL, NULL);
                              break;
                        case 0x3:
                              hItem = m_ResTree.InsertItem(L"图标", NULL, NULL);
                              break;
                        case 0x4:
                              hItem = m_ResTree.InsertItem(L"菜单", NULL, NULL);
                              break;
                        case 0x5:
                              hItem = m_ResTree.InsertItem(L"对话框", NULL, NULL);
                              break;
                        case 0x6:
                              hItem = m_ResTree.InsertItem(L"字符串列表", NULL, NULL);
                              break;
                        case 0x7:
                              hItem = m_ResTree.InsertItem(L"字体目录", NULL, NULL);
                              break;
                        case 0x8:
                              hItem = m_ResTree.InsertItem(L"字体", NULL, NULL);
                              break;
                        case 0x9:
                              hItem = m_ResTree.InsertItem(L"快捷键", NULL, NULL);
                              break;
                        case 0xA:
                              hItem = m_ResTree.InsertItem(L"非格式化资源", NULL, NULL);
                              break;
                        case 0xB:
                              hItem = m_ResTree.InsertItem(L"消息列表", NULL, NULL);
                              break;
                        case 0xC:
                              hItem = m_ResTree.InsertItem(L"鼠标指针组", NULL, NULL);
                              break;
                        case 0xD:
                              hItem = m_ResTree.InsertItem(L"图标组", NULL, NULL);
                              break;
                        case 0xE:
                              hItem = m_ResTree.InsertItem(L"版本信息", NULL, NULL);
                              break;
                        default:
                              str.Format(L"%d", pResDE->Name);
                              hItem = m_ResTree.InsertItem(str, NULL, NULL);
                              break;
                        }
                }


第二层

PIMAGE_RESOURCE_DIRECTORY pResD2 = (PIMAGE_RESOURCE_DIRECTORY)((long)pResD + pResDE->OffsetToDirectory);
            
            DWORD ReSize2 = pResD2->NumberOfIdEntries + pResD2->NumberOfNamedEntries;
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD2 + sizeof(IMAGE_RESOURCE_DIRECTORY));

DWORD SecondOrder = 0;
                for (; SecondOrder < ReSize2; SecondOrder++)
                {
                        if (pResDE2->DataIsDirectory == 1)
                        {
                              //输出第二层资源的标识看是数字还是字符串
                              if (pResDE2->NameIsString == 1)
                              {
                                        PIMAGE_RESOURCE_DIR_STRING_U pREsDStrU2 = (PIMAGE_RESOURCE_DIR_STRING_U)((long)pResD + pResDE2->NameOffset);
                                        //输出资源类型名字
                                        //printf("第二层->资源类型名:%ls   ",pREsDStrU2->NameString);
                                        hSubItem = m_ResTree.InsertItem(pREsDStrU2->NameString, NULL, NULL, hItem);
                                        m_ResTree.SetItemData(hSubItem, SecondOrder);
                              }
                              else
                              {
                                        //printf_s("第二层->资源类型名ID:%d   ",pResDE2->Id);      
                                        str.Format(L"%d", pResDE2->Id);
                                        hSubItem = m_ResTree.InsertItem(str, NULL, NULL, hItem);
                                        m_ResTree.SetItemData(hSubItem, SecondOrder);
                              }


第三层
                  PIMAGE_RESOURCE_DIRECTORY pResD3 = (PIMAGE_RESOURCE_DIRECTORY)((long)pResD + pResDE2->OffsetToDirectory);
                        PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD3 + sizeof(IMAGE_RESOURCE_DIRECTORY));
                        PIMAGE_RESOURCE_DATA_ENTRY pResDataE = (PIMAGE_RESOURCE_DATA_ENTRY)((long)pResD + pResDE3->OffsetToData);
                              //根据第一层 也就是父节点 区分
                              ResThirdInfo.Rva = pResDataE->OffsetToData;
                              ResThirdInfo.Offset = (RvaOffset(pResDataE->OffsetToData));
                              ResThirdInfo.Size = pResDataE->Size;
                        }
                        else
                        {
                              break;
                        }
                        pResDE2++;
                }



获取重定位表信息
PIMAGE_BASE_RELOCATION pBasR = (PIMAGE_BASE_RELOCATION)((long)Buf + RvaOffset(pDatD->VirtualAddress));
重定位有3个成员结构体:
/***************-重定位表-*****************/
//第一个成员的描述
typedef struct _RELOCINFO
{
      DWORD dwRelocRVA;                //需要重定位的相对虚拟地址
      DWORD dwOffset;                        //根据相对虚拟地址算出的文件属性
      BYTE bType;                              //重定位方式
      DWORD dwRelocValue;                //从算出的文件偏移取出的数据 这个数据就是虚拟地址(VA)
      BYTE bData;                        //用刚才的地址(VA)减去基址得到相对虚拟地址RVA 再算下文件偏移 将其中的数据取出 放到这个数组
}RELOCINFO, *PRELOCINFO;
//第二个成员的描述
typedef struct _RELOCAREAINFO
{
      CHAR szSectionName;//区域所在的节名
      DWORD dwAreaRVA;                //区域基址
      DWORD NumberOfReloc;      //这个区域重定位的具体个数
      std::vector<RELOCINFO> VecRelocInfo;//这个区域重定位的具体信息
}RELOCAREAINFO, *PRELOCAREAINFO;
vector<RELOCAREAINFO> m_VecRelocAreaInfo;
//第三个成员的描述
typedef struct _TYPEOFFSET
{
      WORD Offset : 12;                //偏移值
      WORD Type : 4;                //重定位属性(方式)
}TYPEOFFSET, *PTYPEOFFSET;


重定位表信息需要根据区段信息来获取
DWORD RelCalcOffset(DWORD Rva, PIMAGE_NT_HEADERS32 pNtH, char *Tempbuf, PCHAR pName = NULL, PCHAR pData = NULL, int Flag = NULL)
{
      PIMAGE_SECTION_HEADER pSecHTemp = IMAGE_FIRST_SECTION(pNtH);//区段头
      int index = 0;

      while (!(Rva >= pSecHTemp->VirtualAddress&&
                Rva<pSecHTemp->VirtualAddress + pSecHTemp->SizeOfRawData))
      {
               
                if (index>pNtH->FileHeader.NumberOfSections)
                {
                        if (Flag == 2)
                        {
                              return Rva - pNtH->OptionalHeader.ImageBase;
                        }
                        return Rva;
                }
                ++index;
                ++pSecHTemp;
      }
      //获取区段名
      if (pName != NULL)
      {
                memcpy(pName, pSecHTemp->Name, 8);
      }
      v = Rva - pSecHTemp->VirtualAddress + pSecHTemp->PointerToRawData;;
      DWORD iiii = (long)Tempbuf + v;
      //获取数据
      if (pData != NULL)
      {
                if (Flag == 2)
                {
                        return Rva - pNtH->OptionalHeader.ImageBase;
                }
                memcpy(pData, PCHAR((long)Tempbuf + v), 10);
      }
      return v;
}

RELOCAREAINFO Temp = { 0 };
      int j = 0;//索引
      CString str;
      
      while (pBasR->VirtualAddress)
      {
                str.Format(L"%d", j + 1);
                m_ReList1.InsertItem(j, str);
                //区域的虚拟地址输出或获取到其他地方
                Temp.dwAreaRVA = pBasR->VirtualAddress;

                RelCalcOffset(Temp.dwAreaRVA, AllPe.pNtH, AllPe.Buf, Temp.szSectionName, NULL);
                CString str(Temp.szSectionName);
                m_ReList1.SetItemText(j, 1, str);
                str.Format(L"%08X", pBasR->VirtualAddress);
                m_ReList1.SetItemText(j, 2, str);
                Temp.NumberOfReloc = (pBasR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
                str.Format(L"%02X", Temp.NumberOfReloc);
                CString str1;
                str1.Format(L"%d", Temp.NumberOfReloc);
                m_ReList1.SetItemText(j, 3, str + L" " + str1);
                m_VecpBseRInfo.push_back(pBasR);      
                pBasR = (PIMAGE_BASE_RELOCATION)((long)pBasR + pBasR->SizeOfBlock);
                j++;
      }
      
获取TLS表信息

PIMAGE_TLS_DIRECTORY32 pTls = (PIMAGE_TLS_DIRECTORY32)((long)Buf + RvaOffset(pDatD->VirtualAddress));
pTls->StartAddressOfRawData             //源数据起始位置
pTls->EndAddressOfRawData               //源数据终止位置
pTls->AddressOfIndex                  //保存TLS索引的位置
pTls->AddressOfCallBacks                //TLS回调函数的地址表的位置(VA)在.rdata节
pTls->SizeOfZeroFill                  //用0填充TLS变量区域的大小
pTls->Characteristics                   //保留,恒为0


Auller 发表于 2016-10-6 22:35

Hmily 发表于 2016-10-6 22:02
编辑了下,去掉了字体大小的,当还是最好用代码框处理下代码,会好看很多。

谢谢,明天我修改一下

Very_good 发表于 2016-10-7 09:07

wjdxs1 发表于 2016-10-6 21:58
看PE结构的图会好得多。。。。。很直观了就
有时间帮忙看下悬赏谢谢

Hmily 发表于 2016-10-6 21:16

从哪转的,注意下格式。

Auller 发表于 2016-10-6 21:19

Hmily 发表于 2016-10-6 21:16
从哪转的,注意下格式。

自己写的好吗

Auller 发表于 2016-10-6 21:23

附上压缩文档

Very_good 发表于 2016-10-6 21:44

眼花缭乱了

Zero丶冻结 发表于 2016-10-6 21:47

看的有点头晕。

anythink 发表于 2016-10-6 21:51

学习了!!

Hmily 发表于 2016-10-6 21:58

Auller 发表于 2016-10-6 21:19
自己写的好吗

我意思是从哪复制过来的,格式怎么这么乱了。

SeriousSnow 发表于 2016-10-6 21:58

Very_good 发表于 2016-10-6 21:44
眼花缭乱了

看PE结构的图会好得多。。。。。很直观了就

Hmily 发表于 2016-10-6 22:02

编辑了下,去掉了字体大小的,当还是最好用代码框处理下代码,会好看很多。
页: [1] 2
查看完整版本: 自己总结的简单PE结构解析,有需要的可以看看