概述
pe文件:exe dll ocx sys com
pe结构:MS-dos头 标准pe头 扩展pe头 数据目录 节表
MSDOS头结构解析
4D5A 文件标志
#include<stdio.h>
#include<Windows.h>
int main(){
FILE * pFile = NULL;
char * buffer;
int NFileLength = 0;
pFile = fopen(自行设置);
fseek(pFile,0,SEEK_END);
nFileLength = ftell(pFile);
rewind(pFIle);
int imageLength = nFileLength * sizeof(char) + 1;
buffer = (char *)malloc(imageLength);
memset(buffer,0,nFileLength * sizeof(char)+1);
fread(buffer,1,imageLength,pFile);
// _IMAGER_DOS_HEADER //msdos头结构体,可读取
PIMAGE_DOS_HEADER ReadDosHeader;
ReadDosHeader = ( PIMAGE_DOS_HEADER)buffer;
printf("%x",ReadDosHeader->e_magic);
free(buffer);
return 0;
}
e_lfanew 指向新的pe头 偏移量 0x3c
PE文件结构解析
标准PE头和扩展PE头
printf("PEheader\n");
_IMAGE_NT_HEADERS =ReadNTHeaders; //分32 和 64 两个结构体
IMAGE_NT_SIGNATURE = 0x00004550 //PE00
ReadNTHeaders.
常用区段:
- .text (code 代码段
- data 数据段 可读写 全局和静态变量
- rodata 只读数据段
- idata 导入表信息
- edata 导出表
- rsrc 资源段
- bss 未初始化数据
- crt 运行时代码库
- tls 线程局部存储
- reloc 重定位区段
IMAGE_SIZEOF_FILE_HEADER等 //pe中一组宏定义常见属性
VA Virtual Address //虚拟内存地址 00000000h-0fffffffh
进程的基地址 + 相对虚拟内存地址
RVA //Reversc Virtual Address 相对虚拟地址(模块的基地址的偏移量
FOA File Offset Address 文件偏移地址 某个位置距离文件头的偏移量
注意处理文件的位数是32位还是64位
区段表结构解析及遍历
PIMAGE_DOS_HEADER ReadDosHeader;
ReadDosHeader = (PIMAGE_DOS_HEADER)buffer;
ReadNTHeader = (PIMAGE_NT_HEADERS)(buffer + ReadDosHeader->e_lfanew);
//区段解析遍历
PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION(ReadNTHeaders);
PIMAGE_FILE_HEADER pFileHeader = &ReadNTHders->FileHeader;
for(int i = 0 ; i<pFileHeader.NumberOfSections;i++){
printf("name (区段名称):%s\n",ReadSectionHeader[i].Name);
printf("VOffset起始的相对虚拟地址:%x\n", ReadSectionHeader[i].VirtualAddress);
//等等等等
}
数据目录表结构与地址转换函数
数据目录表:
_IMAGE_DATA_DIRECTION
导入表
导出表
资源表
异常目录表
安全目录(签名,证书
重定位表
debug 表
版权信息等表
全局指针偏移目录
线程局部存储表
载入配置表
绑定目录表
导入地址表
延迟导入表
运行时描述符
LOADPE.exe中均有
//计算偏移的函数
#include<stdio.h>
#include<Windows.h>
//dwRva 是某个数据目录表的虚拟地址
//buffer是读取的PE文件的缓冲区
DWORD RvaToOffset(DWORD dwRva, char *buffer){
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//区段表
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
//判断是否落在头部当中
if(dwRva < pSection[0].VirtualAddress){
return dwRva;
}
for(int i = 0;i<pNt->FileHeader.NumberOfSections ; i++){
//判断是否落在某个区段内
if(dwRva>= pSection[i].VirtualAddress && dwRva<=pSection[i].VirtualAddress + pSection[i].Misc.VirtualSize){
//是数据目录表到区段起始地址的偏移量 offset
//pSection[i].PointerToRawData区段到文件头的偏移量
//返回的是数据目录表起始地址到文件头的偏移
return dwRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
}
}
}
//VirtualADDRESS 起始地址
//Size 长度
//VirtualADDRESS + Size 结束地址
导入表结构解析
遍历动态链接库和API
IMAGE_IMPORT_DESCRIPTOR
IMAGE_THUNK_DATA
IMAGE_IMPORT_BY_NAME
//解析导入表函数
void ImportTable(char* buffer){
//Dos
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//定位导入表
PIMAGE_DATA_DIRECTORY pImportDir = (PIMAGE_DATA_DIRECTORY)(pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT)
//填充结构
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(RvaToOffset(pImportDir->VirtualAddress,buffer) + buffer);
//判断导入名称为不为空
while(pImport->Name != NULL){
char * szDLLName = (char *)(RvaToOffset(pImport->Name,buffer) + buffer);//通过rva计算名字地址
printf("DLLname %s\n",szDLLName);
printf("time %08x\n",pImport->TimeDataStamp);
printf("ForwarderChain:%08x\n", pImport->ForwarderChain);
printf("name offset:%08x\n",pImport->Name);
printf("table RVA/FirstThunk:%08x\n",pImport->FirstThunk);
printf("OriginalFirstThunk:%08x\n\n",pImport->OriginalFirstThunk);
//指向导入地址表的RVA
PIMAGE_THUNK_DATA pIat = (PIMAGE_THUNK_DATA)(RvaToOffset(pImport->OriginalFirstThunk,buffer) + buffer);
DWORD index = 0;
DWORD ImportOffset = 0;
//被导入函数的序号
while(pIat->u1.Ordinal != 0){
printf("ThunkRva:%08x\n",pImport->OriginalFirstThunk + index);
ImportOffset = RvaToOffset(pImport->OriginalFirstThunk,buffer);
printf("ThunkOffset:%08x\n",ImportOffset+index);
index += 4;
if(pIat->u1.Ordinal & 0x80000000 != 1){
PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(RvaToOffset(pIat->u1.AddressOfData,buffer)+buffer);
printf("APIname:%s\n",pName->Name);
printf("Hint:%04x\n",pName->Hint);
printf("thunkValue:%08x\n\n",pIat->u1.Function);
//被导入函数的地址↑
}
pIat++;
}
pImport++;
}
}
导出表结构解析
_IMAGE_EXPORT_DIRECTORY
void ExportTable(char * buffer){
//Dos
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//定位数据目录表中的导出表
PIMAGE_DATA_DIRECTORY pExportDir = (PIMAGE_DATA_DIRECTORY)(pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT)
//导出表结构填充
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(RvaToOffset(pExportDir->VirtualAddress,buffer) + buffer )
//名称
char* szName = (char *)(RvaToOffset(pExport->Name,buffer) + buffer)
if(pExport->AddressOfFunctions == 0){
printf("当前没有导出表\n");
return ;
}
printf("导出表OFFSET:%08x \n", RvaToOffset(pExportDir->VirtualAddress,buffer));
printf("特征值:%08x\n", pExport->Characteristics);
printf("基:%08x\n", pExport->Base);
printf("名称的offser:%08x\n",pExport->Name);
//等等基本信息
//函数数量
DWORD dwNumOfFun = pExport->NumberOfFunctions;
//函数名数量
DWORD dwNumOfNames = pExport->NumberOfNames;
//基
DWORD dwBase = pExport->Base;
//导出地址表
PDWORD pEat32 = (PDWORD)(RvaToOffset(pExport->AddressOfFunctions,buffer)+buffer);
//导出名称表
PDWORD pEnt32 = (PDWORD)(RvaToOffset(pExport->AddressOfNames,buffer)+buffer);
//导出序号表
PWORD pId = (PWORD)(RvaToOffset(pExport->AddressOfNameOrdinals,buffer)+buffer);
for(int i =0;i<dwNumOfFun;i++){
if(pEat32[i] == 0){
continue;
}
DOWRD Id = 0;
for(;Id<dwNmuOfNames;Id++){
if(pId[Id] == i){
break;
}
}
if(Id == dwNmuOfNames){
printf("Id:%x Address: 0x%08x Name[NULL] \n",i+dwbase,pEat32[i]);
}
else{
char * szFunName = (char*)(RvaToOffset(pEnt32[i],buffer)+buffer);
printf("Id:%x Address: 0x%08x Name[%s] \n",i+dwbase,pEat32[i],szFunName);
}
}
}
重定位表结构解析
//解析重定位表的函数
主要用于动态链接库
void RelocTable(char * buffer){
//IMAGE_BASE_RELOCATION
// 0x1000的倍数
typedef struct _TYPE{
WORD Offset : 12;
WORD Type:4;
}TYPE *PTYPE;
//Dos
//Dos
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//定位重定位表
PIMAGE_DATA_DISERCTORY pRelocDir = (PIMAGE_DATA_DISERCTORY)(RvaToOffset(pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_BASERELOC));
//填充重定位表的结构
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(RvaToOffset(pRelocDir->VirtualAddress,buffer) + buffer);
//定位区段
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
while(pReloc->SizOfBlock != 0 ){
//找到本块内的第一个0x1000个字节的起始位置
DWORD dwCount = (pReloc->SizeOfBlock-8) / 2;//个数
DWORD dwRva = pReloc->VirtualAddress;
PTYPE pRelocArr=(PTYPE)(pReloc + 1);
printf("区段:%s \n",pSection->Name);
printf("Rva:0x%08x\n",dwRva);
printf("Items:%X h / %d D \n",pReloc->SizeOfBlock,pReloc->SizeOfBlock);
//找到下一个0x1000字节的结构体
pReloc = (PIMAGE_BASE_RELOCATION)((char*)pReloc + pReloc->SizeOfBlock );
for(int i = 0 ;i<dwCount;i++){
PDWORD pData =(PDWORD) (RvaToOffset(pRelocArr[i].Offset+dwRva,buffer) + buffer);
DWORD pDataOffset = RvaToOffset(pRelocArr[i].Offset+dwRva,buffer);
printf("Rva:0x%08x\n",pRelocArr[i].Offset+dwRva);
printf("区段:%08x\n",*pData);
printf("Offset:%08x\n",pDataOffset);
}
}
}
TLS表结构解析
TLS表:线程局部存储,
//解析TLS表的函数
void TLSTable(char * buffer){
// _IMAGE_TLS_DIRECTORY32
// _IMAGE_TLS_DIRECTORY64
//区分32位和64位
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//定位数据目录表中的TLS表
PIMAGE_DATA_DIRECTORY pTLSDir = (pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_TLS);
//填充TLS结构
PIMAGE_TLS_DIRECTORY pTLS = (PIMAGE_TLS_DIRECTORY)(RvaToOffset(pTLSDir->VirtualAddress,buffer) + buffer);
printf("数据块开始VA:%08x\n",pTLS->StartAddressOfRawData);
printf("数据块结束VA:%08x\n",pTLS->EndAddressOfRawData);
printf("索引变量VA:%08x\n",pTLS->AddressOfIndex);
printf("回调表VA:%08x\n",pTLS->AddressOfCallBakcs);
print("填零大小:%08x\n",pTLS->SizeOfZeroFill);
printf("特征值:%08x\n",pTLS->Characteristics);
}
延迟导入表结构解析
_IMAGE_DELAYLOAD_DESCRIPTOR
//解析延迟导入表的函数
void DeLAYImportTable(char* buffer){
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//定位数据目录表中的延迟导入表
PIMAGE_DATA_DIRECTORY pDelayLoadDir = (pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
//填充延迟导入表的数据结构
PIMAGE_DELAYLOAD_DESCRIPTOR pDelayLoad = (PIMAGE_DELAYLOAD_DESCRIPTOR)(RvaToOffset(pDelayLoadDir->VirtualAddress,buffer) + buffer);
while(pDelayLoad->DllNameRVA != NULL){
char * szDllName = (char*)(RvaToOffset(pDelayLoad->DllNameRVA,buffer) + buffer);
printf("DLL name :%s\n",szDllName);
printf("Attributes:%08x\n",pDelayLoad->Attributes);
printf("ModuleHandleRVA:%08x\n",pDelayLoad->ModuleHandleRVA);
printf("ImportAddressTableRVA:%08x\n",pDelayLoad->ImportAddressTableRVA);
printf("BoundImportAddressTableRVA:%08x\n",pDelayLoad->BoundImportAddressTableRVA);
printf("UnloadInformationTableRVA:%08x\n",pDelayLoad->UnloadInformationTableRVA);
printf("TimeDataStamp:%08x\n\n",pDelayLoad->TimeDataStamp);
pDelayLoad++;
}
}
资源表结构解析
//LoadPE查看
//typedef struct _IMAGE_RESOURCE_DIRECTORY{
}
//_IMAGE_RESOURCE_DIRECTORY_ENTRY
//_IMAGE_RESOURCE_DATA_ENTRY
void ResourceTable(){
}
其他数据表的显示
//解析其他表
void DataTable(char* buffer){
//DosHeader
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
//PE Header
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
//定位数据目录表
PIMAGE_DATA_DIRECTORY pTableDir = (pNt->OptionalHeader.DataDirectory + //你想打印的表的宏定义);
printf("xxx\n");
}