滴水三期 PE 资源表解析

在PE(Portable Executable)文件结构中,资源表(Resource Table)位于可选头的数据目录数组(DataDirectory)第3项(索引2),通过IMAGE_DIRECTORY_ENTRY_RESOURCE标识。其RVA地址存储在OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress中。
树状组织结构
资源根目录
├─ 类型目录(RT_ICON/RT_BITMAP/RT_STRING等)
│ ├─ 名称/ID目录
│ │ └─ 语言目录
│ └─ ...
├─ 版本信息(VS_VERSION_INFO)
└─ 其他自定义资源

资源目录:

typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD   Characteristics;//资源属性
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    WORD    NumberOfNamedEntries;
    WORD    NumberOfIdEntries;
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

资源目录项:

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        struct {
            DWORD NameOffset:31;
            DWORD NameIsString:1;
        } DUMMYSTRUCTNAME;
        DWORD   Name;
        WORD    Id;
    } DUMMYUNIONNAME;
    union {
        DWORD   OffsetToData;
        struct {
            DWORD   OffsetToDirectory:31;
            DWORD   DataIsDirectory:1;
        } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

数据项

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

内存遍历流程

编写程序,定位某个资源在PE文件中的位置代码如下:

void Resource()
{
	int fileSize = 0;
	DWORD ResourceTableAddress = 0;
	LPVOID pFileBuffer = NULL;
	//将文件读取到缓冲区

	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pPEHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	PIMAGE_RESOURCE_DIRECTORY ResourceTable, ResourceTable_Buf, ResourceTable2, ResourceTable3 = NULL;//资源表

	fileSize = ReadPEFile(FilePath, &pFileBuffer);


	if (fileSize == 0)
	{
		printf("ReadPEFile失败!!\n");
	}

	//DOS头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	//NT头
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
	//标准PE头
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
	//可选PE头
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
	//首个节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

    //定位资源表
	ResourceTableAddress = RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
	
	ResourceTable = (PIMAGE_RESOURCE_DIRECTORY)((PBYTE)pFileBuffer + ResourceTableAddress);

	ResourceTable_Buf = ResourceTable;//备份数据方便后面计算用

	DWORD ResourceNumber = ResourceTable->NumberOfIdEntries + ResourceTable->NumberOfNamedEntries;
    //遍历一层目录
	for (int i = 0; i < ResourceNumber; i++)
	{    
		PIMAGE_RESOURCE_DIRECTORY_ENTRY Data = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((PBYTE)ResourceTable + 0x10+ i * 0x08);

		if (!Data->NameIsString)
		{
			printf("Data->Id: %d\n", Data->Id);
		}
		else
		{
			PIMAGE_RESOURCE_DIR_STRING_U pstcString = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)ResourceTable_Buf + Data->NameOffset);
			WCHAR szStr[MAX_PATH] = { 0 };
			memcpy_s(szStr, MAX_PATH, pstcString->NameString, pstcString->Length * sizeof(WCHAR));
			printf("资源字符串: %ls\n", szStr);
		}
		//遍历二层目录
		ResourceTable2= (PIMAGE_RESOURCE_DIRECTORY)((PBYTE)ResourceTable_Buf + Data->OffsetToDirectory);
		DWORD Number2 = ResourceTable2->NumberOfIdEntries + ResourceTable2->NumberOfNamedEntries;
		printf("Number2->%d\n", Number2);
		for (int t = 0; t < Number2; t++)
		{
			PIMAGE_RESOURCE_DIRECTORY_ENTRY Data2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((PBYTE)ResourceTable2 + 0x10 + t * 0x08);

			if (!Data2->NameIsString)
			{
				printf("Data2->Id: %d\n", Data2->Id);
			}
			else
			{
				PIMAGE_RESOURCE_DIR_STRING_U pstcString = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)ResourceTable_Buf + Data2->NameOffset);
				WCHAR szStr[MAX_PATH] = { 0 };
				memcpy_s(szStr, MAX_PATH, pstcString->NameString, pstcString->Length * sizeof(WCHAR));
				printf("资源字符串: %ls\n", szStr);
			}

			遍历三层目录
			ResourceTable3 = (PIMAGE_RESOURCE_DIRECTORY)((PBYTE)ResourceTable_Buf + Data2->OffsetToDirectory);
			DWORD Number3 = ResourceTable3->NumberOfIdEntries + ResourceTable3->NumberOfNamedEntries;
			printf("Number3->%d\n", Number3);
			
			for (int C = 0; C < Number3; C++)
			{
                //获取数据条目地址
				PIMAGE_RESOURCE_DIRECTORY_ENTRY Data3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((PBYTE)ResourceTable3 + 0x10 + C * 0x08);
				
				PIMAGE_DATA_DIRECTORY DATA_DIRE = (PIMAGE_DATA_DIRECTORY)((PBYTE)ResourceTable_Buf + Data3->OffsetToDirectory);
				printf("->->->数据RVA:%x\t数据大小:%x\n", DATA_DIRE->VirtualAddress, DATA_DIRE->Size);

			}

		}



	}

}
posted @ 2025-03-10 00:06  B_1219  阅读(95)  评论(0)    收藏  举报