自己总结的简单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
Hmily 发表于 2016-10-6 22:02
编辑了下,去掉了字体大小的,当还是最好用代码框处理下代码,会好看很多。
谢谢,明天我修改一下 wjdxs1 发表于 2016-10-6 21:58
看PE结构的图会好得多。。。。。很直观了就
有时间帮忙看下悬赏谢谢 从哪转的,注意下格式。 Hmily 发表于 2016-10-6 21:16
从哪转的,注意下格式。
自己写的好吗 附上压缩文档 眼花缭乱了 看的有点头晕。 学习了!! Auller 发表于 2016-10-6 21:19
自己写的好吗
我意思是从哪复制过来的,格式怎么这么乱了。 Very_good 发表于 2016-10-6 21:44
眼花缭乱了
看PE结构的图会好得多。。。。。很直观了就 编辑了下,去掉了字体大小的,当还是最好用代码框处理下代码,会好看很多。
页:
[1]
2