卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章1829本站已运行4109

c语言解析PE结构

在pe结构中最主要的是有三个 Dos头,Nt头,节头。这三个头记录了所有的信息。
Dos头
关于dos头中没有太多需要讲的东西,这个头部是为了兼容老系统留下来的。在这个头部只存在两个有意义的字段。e_magic 它的值是MZ, 二进制值是5A4D。还有一个字段是e_lfanew 这个字段是指向nt头开始的位置。
获取dos头数据的代码
        PIMAGE_DOS_HEADER pDosHead = NULL;
        pDosHead = (PIMAGE_DOS_HEADER)imageBase;

        printf("DOS头:                                0x%.8x\n", pDosHead->e_magic);
        printf("文件地址:                        0x%.8x\n", pDosHead->e_lfarlc);
        printf("PE结构偏移:                        0x%.8x\n", pDosHead->e_lfanew);
nt头
在nt头中又分为Signature字段,文件头,可选头。
Signature 默认是0x00004550 程序位数不同这个数值其实是不一样的,x86的下是0x10B x64下是 0x20B 。
可以用这里位置判断exe程序是32位的还是64位的。

文件头
在文件头中有以下几个字段
字段名 字段值 字段代表的意义
Machine 由编译决定也可指定 运行平台
NumberOfSections 编译器填充也可自己填充 文件的节数目
TimeDateStamp 编译器填充也可自己填充 文件创建日期和时间
PointerToSymbolTable 编译器填充也可自己填充 指向符号表(用于调试)
NumberOfSymbols 编译器填充也可自己填充 符号表中的符号数量
SizeOfOptionalHeader 编译器填充也可自己填充t 可选头的长度
Characteristics 编译器填充也可自己填充 文件的属性 exe=0x010h dll=0x210h

在该字段中比较重要的字段主要有  NumberOfSections 、Characteristics。前者是用来遍历节表数的依据,后者可以用来判断当前文件是一个exe程序还是一个dll库。

获取文件头数据的代码
        PIMAGE_NT_HEADERS pNtHead = NULL;
        PIMAGE_FILE_HEADER pFileHead = NULL;

        pNtHead = GetNtHead(imageBase);
        pFileHead = &pNtHead->FileHeader;

        printf("运行平台:\t\t0x%.8x\n", pFileHead->Machine);
        printf("节区数目:\t\t0x%.8x\n", pFileHead->NumberOfSections);
        printf("时间标记:\t\t%.8d\n", pFileHead->TimeDateStamp);
        printf("可选头大小:\t\t0x%.8x\n", pFileHead->SizeOfOptionalHeader);
        printf("文件特性:\t\t0x%.8x\n", pFileHead->Characteristics);
可选头
在可选头中的数据成员
字段名 字段值 字段意义
Magic 0x10B 或0x20B
MajorLinkerVersion 连接器版本
MinorLinkerVersion
SizeOfCode 所有包含代码节的总大小
SizeOfInitializedData 所有已初始化数据的节总大小
SizeOfUninitializedData 所有未初始化数据的节总大小
AddressOfEntryPoint 程序执行入口RVA
BaseOfCode 代码节的起始RVA
BaseOfData 数据节的起始RVA
ImageBase 程序镜像基地址
SectionAlignment 内存中节的对其粒度
FileAlignment 文件中节的对其粒度
MajorOperatingSystemVersion 操作系统主版本号
MinorOperatingSystemVersion 操作系统副版本号
MajorImageVersion 可运行于操作系统的最小版本号
MinorImageVersion
MajorSubsystemVersion 可运行于操作系统的最小子版本号
MinorSubsystemVersion
Win32VersionValue
SizeOfImage 内存中整个PE映像尺寸
SizeOfHeaders 所有头加节表的大小
CheckSum
Subsystem
DllCharacteristics
SizeOfStackReserve 初始化时堆栈大小
SizeOfStackCommit
SizeOfHeapReserve
SizeOfHeapCommit
LoaderFlags
NumberOfRvaAndSizes 数据目录的结构数量
DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]  
在这个部分中重要的部分已经用中标识说明。
还有一个更重要的部分是DataDirectory 这个后面再说。

获取可选头数据的代码
PIMAGE_NT_HEADERS pNtHead = NULL;
pNtHead = GetNtHead(ImageBase);
printf("入口点:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.AddressOfEntryPoint);

printf("镜像基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.ImageBase);
printf("镜像大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfImage);
printf("代码基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.BaseOfCode);
printf("区块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.SectionAlignment);
printf("文件块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.FileAlignment);

printf("子系统:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.Subsystem);

printf("区段数目:\t\t0x%.8x\n", pNtHead->FileHeader.NumberOfSections);
printf("时间日期标志:\t\t0x%.8x\n", pNtHead->FileHeader.TimeDateStamp);
printf("首部大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfHeaders);

printf("特征值:\t\t\t0x%.8x\n", pNtHead->FileHeader.Characteristics);
printf("校验和:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.CheckSum);

printf("可选头部大小:\t\t0x%.8x\n", pNtHead->FileHeader.SizeOfOptionalHeader);
printf("RVA 数及大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.NumberOfRvaAndSizes);
printf("是否启用随机地址:\t0x%.8x\n", pNtHead->OptionalHeader.DllCharacteristics);
节头
在节表头中的数据字段
字段名 字段值 字段意义
Name[IMAGE_SIZEOF_SHORT_NAME] 节区名称
Misc 节区尺寸
VirtualAddress 节区RVA
SizeOfRawData 在文件中对齐后的尺寸
PointerToRawData 在文件中的偏移
PointerToRelocations  
PointerToLinenumbers
NumberOfRelocations
NumberOfLinenumbers
Characteristics 节区属性字段
在读取该段的数据的代码
PIMAGE_NT_HEADERS pNtHead = NULL;
PIMAGE_FILE_HEADER pFileHead = NULL;
PIMAGE_SECTION_HEADER pSection = NULL;
DWORD NumberOfSectinsCount = 0;
pNtHead = GetNtHead(ImageBase);
pSection = IMAGE_FIRST_SECTION(pNtHead);
pFileHead = &pNtHead->FileHeader;

NumberOfSectinsCount = pFileHead->NumberOfSections;        // 获得区段数量
DWORD* difA = NULL;   // 虚拟地址开头
DWORD* difS = NULL;   // 相对偏移(用于遍历)
difA = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
difS = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));

printf("节区名称 相对偏移\t虚拟大小\t实际偏移\t实际大小\t节区属性\n");
for (int temp = 0; temp < NumberOfSectinsCount; temp++, pSection++)
{
        printf("%s\t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X\n",
                pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,
                pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);

        difA[temp] = pSection->VirtualAddress;
        difS[temp] = pSection->VirtualAddress - pSection->PointerToRawData;
}
        free(difA);
        free(difS);
        difA = NULL;
        difS = NULL;
在pe中除了以上这些,还有一些重要的东西,比如导入表,导出表(存在dll文件中,exe中也有不过该位置数据为空),RVA到FOA(FOA到RVA),IAT表(IAThook,以及脱壳中IAT修复)。
后面这些下回再发

最终完整代码
#include<stdio.h>
#include<Windows.h>
#include<ImageHlp.h>
#pragma comment(lib,"ImageHlp.lib")
HANDLE OpenPeByName(LPTSTR fileName) {
 
        LPTSTR peFile = fileName;
 
        HANDLE hFile, hMapFile, lpMapAddress = NULL;
        DWORD dwFileSize = 0;
 
        hFile = CreateFile(peFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
                printf("[-] 文件打开失败\n");
                exit(0);
        }
        dwFileSize = GetFileSize(hFile, NULL);
        if (dwFileSize != 0) {
                printf("[+] 文件已读入!\n");
        }
        hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
        if (hMapFile == NULL) {
                printf("[-]创建文件映射对象失败!!\n");
                exit(0);
        }
        lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
        if (lpMapAddress != NULL) {
                return lpMapAddress;
        }
 
 
}
 
void displayDosHeadInfo(HANDLE imageBase)
{
        PIMAGE_DOS_HEADER pDosHead = NULL;
        pDosHead = (PIMAGE_DOS_HEADER)imageBase;
 
        printf("DOS头:                                0x%.8x\n", pDosHead->e_magic);
        printf("文件地址:                        0x%.8x\n", pDosHead->e_lfarlc);
        printf("PE结构偏移:                        0x%.8x\n", pDosHead->e_lfanew);
 
 
 
}
PIMAGE_NT_HEADERS GetNtHead(HANDLE imageBase)
{
        PIMAGE_DOS_HEADER pDosHead = NULL;
        PIMAGE_NT_HEADERS pNtHead = NULL;
 
        pDosHead = (PIMAGE_DOS_HEADER)imageBase;
        //如果该程序是64位的 pNtHead = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHead + pDosHead->e_lfanew);
        pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 
        return pNtHead;
}
 
 
void displayFileHeadInfo(HANDLE imageBase) {
        PIMAGE_NT_HEADERS pNtHead = NULL;
        PIMAGE_FILE_HEADER pFileHead = NULL;
 
        pNtHead = GetNtHead(imageBase);
        pFileHead = &pNtHead->FileHeader;
 
        printf("运行平台:\t\t0x%.8x\n", pFileHead->Machine);
        printf("节区数目:\t\t0x%.8x\n", pFileHead->NumberOfSections);
        printf("时间标记:\t\t%.8d\n", pFileHead->TimeDateStamp);
        printf("可选头大小:\t\t0x%.8x\n", pFileHead->SizeOfOptionalHeader);
        printf("文件特性:\t\t0x%.8x\n", pFileHead->Characteristics);
}
 
void DisplayOptionalHeaderInfo(HANDLE ImageBase)
{
        PIMAGE_NT_HEADERS pNtHead = NULL;
        pNtHead = GetNtHead(ImageBase);
        printf("入口点:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.AddressOfEntryPoint);
 
        printf("镜像基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.ImageBase);
        printf("镜像大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfImage);
        printf("代码基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.BaseOfCode);
        printf("区块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.SectionAlignment);
        printf("文件块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.FileAlignment);
 
        printf("子系统:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.Subsystem);
 
        printf("区段数目:\t\t0x%.8x\n", pNtHead->FileHeader.NumberOfSections);
        printf("时间日期标志:\t\t0x%.8x\n", pNtHead->FileHeader.TimeDateStamp);
        printf("首部大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfHeaders);
 
        printf("特征值:\t\t\t0x%.8x\n", pNtHead->FileHeader.Characteristics);
        printf("校验和:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.CheckSum);
 
        printf("可选头部大小:\t\t0x%.8x\n", pNtHead->FileHeader.SizeOfOptionalHeader);
        printf("RVA 数及大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.NumberOfRvaAndSizes);
        printf("是否启用随机地址:\t0x%.8x\n", pNtHead->OptionalHeader.DllCharacteristics);
}
 
void DisplaySectionHeaderInfo(HANDLE ImageBase)
{
        PIMAGE_NT_HEADERS pNtHead = NULL;
        PIMAGE_FILE_HEADER pFileHead = NULL;
        PIMAGE_SECTION_HEADER pSection = NULL;
        DWORD NumberOfSectinsCount = 0;
        pNtHead = GetNtHead(ImageBase);
        pSection = IMAGE_FIRST_SECTION(pNtHead);
        pFileHead = &pNtHead->FileHeader;
 
        NumberOfSectinsCount = pFileHead->NumberOfSections;        // 获得区段数量
        DWORD* difA = NULL;   // 虚拟地址开头
        DWORD* difS = NULL;   // 相对偏移(用于遍历)
        difA = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
        difS = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
 
        printf("节区名称 相对偏移\t虚拟大小\t实际偏移\t实际大小\t节区属性\n");
        for (int temp = 0; temp < NumberOfSectinsCount; temp++, pSection++)
        {
                printf("%s\t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X\n",
                        pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,
                        pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);
 
                difA[temp] = pSection->VirtualAddress;
                difS[temp] = pSection->VirtualAddress - pSection->PointerToRawData;
        }
        free(difA);
        free(difS);
        difA = NULL;
        difS = NULL;
}
 
void showPeInfoFunction() {
    HANDLE lpMapAddress = NULL;
    wchar_t exePath[] = L"./x32dbg.exe";
    lpMapAddress = OpenPeByName(exePath);
 
    printf("================================================\n");
    printf("Dos head info:\n");
    displayDosHeadInfo(lpMapAddress);
 
 
     
    printf("================================================\n");
    printf("Nt File head info:\n");
    displayFileHeadInfo(lpMapAddress);
 
    printf("================================================\n");
    printf("Nt Optional head info:\n");
    DisplayOptionalHeaderInfo(lpMapAddress);
 
 
    printf("================================================================================================\n");
    printf("Section head info:\n");
    DisplaySectionHeaderInfo(lpMapAddress);
}
 
卓越飞翔博客
上一篇: python管理QQ群代码
下一篇: Python密码本连接wifi
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏