pe文件:exe dll ocx sys com
pe结构:MS-dos头 标准pe头 扩展pe头 数据目录 节表
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\
");_IMAGE_NT_HEADERS =ReadNTHeaders; //分32 和 64 两个结构体IMAGE_NT_SIGNATURE = 0x00004550 //PE00ReadNTHeaders.
常用区段:
.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\
",ReadSectionHeader[i].Name); printf("VOffset起始相对虚拟地址:%x\
", 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_DESCRIPTORIMAGE_THUNK_DATAIMAGE_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\
",szDLLName); printf("time \bx\
",pImport->TimeDataStamp); printf("ForwarderChain:\bx\
", pImport->ForwarderChain); printf("name offset:\bx\
",pImport->Name); printf("table RVA/FirstThunk:\bx\
",pImport->FirstThunk); printf("OriginalFirstThunk:\bx\
\
",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:\bx\
",pImport->OriginalFirstThunk index); ImportOffset = RvaToOffset(pImport->OriginalFirstThunk,buffer); printf("ThunkOffset:\bx\
",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\
",pName->Name); printf("Hint:x\
"
;,