吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1135|回复: 7
收起左侧

[C&C++ 原创] 用c语言解析PE结构

[复制链接]
JackLSQ 发表于 2024-1-14 23:00
本帖最后由 JackLSQ 于 2024-1-14 23:03 编辑

最近在学习Windows 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

在该字段中比较重要的字段主要有  NumberOfSectionsCharacteristics。前者是用来遍历节表数的依据,后者可以用来判断当前文件是一个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修复)。

后面这些下回再发


pe输出信息

pe输出信息



最终完整代码

[C] 纯文本查看 复制代码
#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);
}

免费评分

参与人数 4吾爱币 +8 热心值 +4 收起 理由
wushaominkk + 5 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
a3859495 + 1 + 1 谢谢@Thanks!
xaolong621 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
tomhex + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ztqddj007 发表于 2024-1-15 08:42
有点不懂 学习一下
w759003376 发表于 2024-1-15 09:27
ciker_li 发表于 2024-1-15 10:33
Fixbluesky 发表于 2024-1-15 11:08

感谢分享
APWN 发表于 2024-1-15 11:44
底层的一些知识,学习一下,谢谢楼主!
w759003376 发表于 2024-1-18 09:45
感谢楼主分享,可以的
糖糖糖 发表于 2024-2-7 18:28
感谢楼主的分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-29 23:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表