优秀的编程知识分享平台

网站首页 > 技术文章 正文

PE文件结构解析(磁盘)(pe文件夹)

nanyue 2024-09-05 18:13:53 技术文章 6 ℃

概述

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");
    
}



Tags:

最近发表
标签列表