PE文件(十三)资源表

news2025/4/2 21:36:28

所谓的资源也就是我们之前学的MFC中的对话框,按钮,编辑框之类的东西。不仅MFC有资源,我们平时熟悉的控制台程序也有资源

当我们平时写一些程序或者木马时,我们通常对其定义一个随机的名称或者路径,然后再向外界进行释放(资源的 二进制数据写入文件)。而这些随机的名称或路径被写入PE文件的资源中

资源释放

我们以一个控制台程序为例,添加一个exe文件资源然后再进行资源释放:

首先我们先添加一个资源:资源文件->添加资源->导入->某exe文件->自定义资源类型,此时便会加载该exe文件的二进制数据

代码演示

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
#include "resource.h"

//随机资源名称
LPCSTR RandomString() 
{
	srand((int)time(NULL));//设置随机数起始值
	char szStr[] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' };//设置随机名称的字符
	char Temp[9] = { 0 };
	for (size_t i = 0; i < 7; i++)
	{
		int nIndex = rand();//设置随机数
		nIndex = nIndex % 26;//获取对应名称字符的随机数
		Temp[i] = szStr[nIndex];//获取随机数对应字符
	}
	return Temp;
}
//释放
void ReleaseFile(LPCSTR lpFilePath)
{
	HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_INJECTEXE1), "injectexe");//获取添加的资源
	DWORD dwSize = SizeofResource(NULL, hRsrc);//获取资源大小
	HGLOBAL hGlobal = LoadResource(NULL, hRsrc);//加载资源
	LPVOID lpRes = LockResource(hGlobal);//获取资源内存二进制数据
	HANDLE hFile = CreateFile(lpFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建文件
	DWORD dwWriteLen = 0;
	WriteFile(hFile, lpRes, dwSize, &dwWriteLen, NULL);//将资源的二进制数据写入文件
	CloseHandle(hFile);//关闭文件句柄
}

int main()
{
	char szFilePath[50] = "D:\\";
	char szFileEnd[5] = ".exe";
	LPCSTR szFileName = new char[50];
	strcpy((char *)szFileName, RandomString());
	strcat(szFilePath, szFileName);
	strcat(szFilePath, szFileEnd);//拼接资源名称
	ReleaseFile(szFilePath);
	system("pause");
	return 0;
}

此时我们生成的文件可以正常运行

资源表

资源表是一张描述资源数据在PE中的分布情况的表,其包含了PE文件中所使用到的资源。通过资源表,操作系统和应用程序可以轻松地访问和加载可执行文件中的资源。开发人员可以使用资源编辑器或编程工具来创建、编辑和管理资源表中的资源。

资源表定位

通过数据目录的第3个目录项,我们可以得知资源表所在地址RVA及其数据大小,从而定位到资源表所在地址

从资源表的起始开始为一级子目录的资源目录头,紧接着是各个一级子目录的资源目录项。每个一级子目录都可以指向一个二级子目录,起始为二级子目录的资源目录头,紧接着是各个二级子目录的资源目录项。每个而级子目录都可以指向一个三级子目录,起始为三级子目录的资源目录头,紧接着是各个三级子目录的资源目录项。每个三级子目录都可以指向一个资源或者代码页

三级目录结构

PE文件组织资源的方式类似于操作系统的文件管理方式。从根目录开始向下分为三级管理:一级子目录、二级子目录和三级子目录,三级子目录下为具体资源。如图所示:

1.一级子目录按照资源类型分类,如光标一级子目录,位图一级子目录等多个资源类型。

2.二级子目录按照资源的ID分类。例如:在菜单一级子目录下可以有: IDM_OPEN的ID为2001,IDM_EXIT的ID为2002,IDM1的ID为4000等多个菜单项。

3.三级子目录是按照资源的代码页分类,即不同的语言代码页对应不同的数据。其中,根据语言可以分为简体中文、英文、繁体中文等多个代码页。一般来说这个目录没啥用,这样用该目录下的文件

4.三级子目录后即为文件(资源),三级子目录和文件一一对应。文件是包含了资源数据的指针和大小等信息的一个数据结构。对所有资源数据块的访问均可从这里开始。

资源表结构

资源表的结构分为如下四个部分:

资源目录表(根目录):资源目录表是资源表的顶层结构,它包含了指向各个资源类型的目录项的指针。

资源类型目录表(一级子目录):资源类型目录表包含了每个资源类型的目录项,例如位图、图标、对话框等。

资源名称/标识符目录表(二级子目录):资源名称/标识符目录表包含了每个资源类型下的资源名称或标识符的目录项。

资源数据目录表(资源):资源数据目录表包含了每个资源的实际数据的位置和大小。

资源目录头

资源目录头,它标识了当前级别目录整体信息,如:属性,创建日期,版本目录项的数量等等描述信息。

资源目录头结构如下:

typedef struct _IMAGE_RESOURCE_DIRECTORY {
	DWORD   Characteristics;				//资源特性
    DWORD   TimeDateStamp;					//时间戳
    WORD    MajorVersion;					//资源的大版本号
    WORD    MinorVersion;					//资源的小版本号
    WORD    NumberOfNamedEntries;			//以字符串命名的入口数量
    WORD    NumberOfIdEntries;				//以ID命名的入口数量
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

资源目录项

紧接着资源目录头的是资源目录项。一个资源目录可以有多个资源目录项(以名称定义的资源目录项或以ID定义的资源目录项,或者两者组合)。目录项和目录项之间按照线性排列:首先按照字母升序(不分大小写)排列名称资源目录项, 然后再按ID升序排列ID资源目录项。

资源目录项分为两种表示类型,分别为名称和ID。名称为以字符串形式命名的资源,一般为用户自定义的。ID条目为以整数形式命名的资源,一般为操作系统为我们预定义

 如下的操作系统为我们预定义的一级子目录的ID类型的类型数据,ID = 0到ID = 0x11

const char * g_ResType[0x11] = 
{
    "NULL",
	"鼠标指针",
	"位图",
	"图标",
	"菜单",
	"对话框",
	"字符串列表",
	"字体目录",
	"字体",
	"快捷键",
	"非格式化资源",
	"消息列表",
	"鼠标指针组",
	"NULL",
	"图标组",
	"NULL",
	"版本信息",
};

资源目录项结构如下:

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        struct {
            DWORD NameOffset:31;//低位:当高位为1时,该值为一个包含名称的结构体相对于一级子目录的资源目录头的偏移。当高位为0时,该值为一个编号
            DWORD NameIsString:1;//高位:当为1时,资源类型为名称。当为0时,资源类型为ID
        } DUMMYSTRUCTNAME;
        DWORD   Name;    //当NameIsString为1时,该值为ID,当NameIsString为0时,该值为一个包含名称的结构体相对于一级子目录的资源目录头的偏移。一般不用该成员
        WORD    Id;      //资源ID
    } DUMMYUNIONNAME;
    union {
        DWORD   OffsetToData;
        struct {
            DWORD   OffsetToDirectory:31;//低位:高位为1时,该值为下一级目录或资源相对于一级子目录的资源目录头的偏移。
            DWORD   DataIsDirectory:1;//高位:当为1时,该值表示下一级目录或资源
        } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

由于一级子目录下的DataIsDirectory为1指向二级子目录。而二级子目录下的DataIsDirectory为1指向资源

包含union字符串的结构体如下:

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
    WORD    Length;
    WCHAR   NameString[ 1 ];
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

注意:NameString并不是以0结尾的字符串数组,因此我们在使用时,需要把它复制到一个新的数组中,以添加结尾的0

资源数据项

资源数据项即三级目录结构中的文件,它是通过三次目录定位后找到的一个数据结构,其结构如下:

typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
    DWORD   OffsetToData; //文件偏移
    DWORD   Size;         //资源大小
    DWORD   CodePage;     //代码页
    DWORD   Reserved;     //保留字段
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;

通过资源目录项的OffsetToData,我们可以找到具体的资源。

资源表图示

 我们通过LoadPE观察一个PE文件的资源表:

如上图所示:

AFX_DIALOG_LAYOUT为一级子目录的资源目录项的类型信息

102为二级子目录的资源目录项的类型信息

图中并没有显示三级子目录,一般来说三级子目录的作用就是指向资源,因此常常把它忽略

Root Rirectory为根目录的资源目录头的相关信息

Selected Rirectory为一级子目录的资源目录头的相关信息

Selected Item为三级子目录下资源的相关信息

遍历资源表

代码实现:

static const char* szResName[0x11]
{
    0,
    "Corsor",
    "Bitmap",
    "Icon",
    "Menu",
    "Dialog",
    "StringTable",
    "FontDir",
    "Font",
    "Accelerator",
    "RCDATA",
    "MessageTable",
    "GroupCursor",
    "zz",
    "GroupIcon",
    "xx",
    "Version"
};

void PrintResourceTable()
{
    PIMAGE_DOS_HEADER pDosHeader = nullptr;
    PIMAGE_NT_HEADERS pNTHeader = nullptr;
    PIMAGE_FILE_HEADER pFileHeader = nullptr;
    PIMAGE_OPTIONAL_HEADER pOptionalHeader = nullptr;

    PIMAGE_DATA_DIRECTORY pDataDirectory = nullptr;
    PIMAGE_RESOURCE_DIRECTORY pResourceTable = nullptr;
    PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceEntry = nullptr;

    LPVOID pFileBuffer = nullptr;
    DWORD dwSize = 0;
    
    dwSize = ReadFile(&pFileBuffer, FILE_PATH_IN);
    if (dwSize == 0 || pFileBuffer == nullptr)
    {
        printf("读取文件失败");
    }

    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
    pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + sizeof(pNTHeader->Signature));
    pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);

    //定位资源表
    pDataDirectory = (PIMAGE_DATA_DIRECTORY)(&pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
    pResourceTable = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pFileBuffer + RVA2FOA(pDataDirectory->VirtualAddress, pFileBuffer));
    pResourceEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceTable + 1);

    //解析第一层
    DWORD dwTypeCount = pResourceTable->NumberOfIdEntries + pResourceTable->NumberOfNamedEntries;
    for (DWORD i = 0; i < dwTypeCount; i++)
    {
        //最高位为0
        if (pResourceEntry[i].NameIsString == 0)
        {
            if (pResourceEntry[i].Id < 0x11)
            {
                printf("资源类型ID:%d %s\n", pResourceEntry[i].Id, szResName[pResourceEntry[i].Id]);
            }
            else
            {
                printf("资源类型ID:%d\n", pResourceEntry[i].Id);
            }
        }
        //最高位为1
        else if (pResourceEntry[i].NameIsString == 1)
        {
            PIMAGE_RESOURCE_DIR_STRING_U pStr = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pResourceTable + pResourceEntry[i].NameOffset);
            WCHAR szStr[MAX_PATH] = { 0 };
            memcpy(szStr, pStr->NameString, pStr->Length * sizeof(WCHAR));
            printf("资源类型名称:%ls\n", szStr);
        }

        //解析第二层
        if (pResourceEntry[i].DataIsDirectory == 1)
        {
            printf("第二层目录偏移:%x\n", pResourceEntry[i].OffsetToDirectory);
            PIMAGE_RESOURCE_DIRECTORY pRes2 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pResourceTable + pResourceEntry[i].OffsetToDirectory);
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes2 + 1);
            DWORD dwCount = pRes2->NumberOfIdEntries + pRes2->NumberOfNamedEntries;

            for (DWORD i = 0; i < dwCount; i++)
            {
                //最高位为0
                if (pResEntry2[i].NameIsString == 0)
                {
                    printf("  ->资源标识ID:%d\n", pResEntry2[i].Id);
                }
                else
                {
                    PIMAGE_RESOURCE_DIR_STRING_U pStr = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pResourceTable + pResEntry2[i].NameOffset);
                    WCHAR szStr[MAX_PATH] = { 0 };
                    memcpy(szStr, pStr->NameString, pStr->Length * sizeof(WCHAR));
                    printf("  ->资源名称:%ls\n", szStr);
                }

                //解析第三层
                if (pResEntry2[i].DataIsDirectory == 1)
                {

                    PIMAGE_RESOURCE_DIRECTORY pRes3 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pResourceTable + pResEntry2[i].OffsetToDirectory);
                    PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes3 + 1);
                    printf("    -->代码页标号为:%x\n", pResEntry3->Id);
                    if (pResEntry3->DataIsDirectory == 0)
                    {
                        PIMAGE_RESOURCE_DATA_ENTRY pResDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)pResourceTable+pResEntry3->OffsetToData);
                        printf("    --数据RVA:%x\n", pResDataEntry->OffsetToData);
                        printf("    --数据大小:%x\n", pResDataEntry->Size);
                    }
                }
            }
            
        }
    }
}

作业

利用MFC遍历资源表,用树进行显示记录



// ResTreeDlg.h: 头文件
//

#pragma once
#include<iostream>
#include<Windows.h>

// CResTreeDlg 对话框
class CResTreeDlg : public CDialogEx
{
// 构造
public:
	CResTreeDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_RESTREE_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CTreeCtrl m_ResTree;
	CString RootNameEntries;
	CString FirstNameEntries;
	CString RootIDEntries;
	CString FirstIDEntries;
	CString ResRVA;
	CString ResFOA;
	CString ResSize;
	afx_msg void OnOpenFile();
	char* LoadFile(const WCHAR* szFilePath);
	char* szFileBuffer;
	DWORD RvaToFoa(DWORD dwRva, char* szBuffer);
	void OnInitResTree();
	DWORD dwFirstNameEntrys = 0;
	DWORD dwFirstIDEntrys = 0;
};


// ResTreeDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "ResTree.h"
#include "ResTreeDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult);
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
	ON_NOTIFY(TVN_SELCHANGED, IDC_TREE1, &CAboutDlg::OnTvnSelchangedTree1)
END_MESSAGE_MAP()


// CResTreeDlg 对话框



CResTreeDlg::CResTreeDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_RESTREE_DIALOG, pParent)
	, RootNameEntries(_T(""))
	, FirstNameEntries(_T(""))
	, RootIDEntries(_T(""))
	, FirstIDEntries(_T(""))
	, ResRVA(_T(""))
	, ResFOA(_T(""))
	, ResSize(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CResTreeDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TREE1, m_ResTree);
	DDX_Text(pDX, IDC_EDIT1, RootNameEntries);
	DDX_Text(pDX, IDC_EDIT3, FirstNameEntries);
	DDX_Text(pDX, IDC_EDIT2, RootIDEntries);
	DDX_Text(pDX, IDC_EDIT4, FirstIDEntries);
	DDX_Text(pDX, IDC_EDIT5, ResRVA);
	DDX_Text(pDX, IDC_EDIT6, ResFOA);
	DDX_Text(pDX, IDC_EDIT7, ResSize);
}

BEGIN_MESSAGE_MAP(CResTreeDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_COMMAND(ID_32771, &CResTreeDlg::OnOpenFile)
END_MESSAGE_MAP()


// CResTreeDlg 消息处理程序

BOOL CResTreeDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CResTreeDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CResTreeDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CResTreeDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CResTreeDlg::OnOpenFile()
{
	// TODO: 在此添加命令处理程序代码
	CFileDialog file(TRUE, L"exe", L"*.exe", OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY, L"可执行文件|.exe|所有文件|.*", NULL);
	if (file.DoModal() == IDOK)
	{
		CString FilePath = file.GetPathName();
		szFileBuffer = LoadFile(FilePath.GetBuffer());
		AfxMessageBox(L"打开文件成功");
		OnInitResTree();
	}
	else
	{
		AfxMessageBox(L"打开文件失败");
	}
	
}

char* CResTreeDlg::LoadFile(const WCHAR* szFilePath)
{
	HANDLE hFile = CreateFileW(szFilePath, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	DWORD dwFileSize = GetFileSize(hFile, NULL);
	char* szBuffer = new char[dwFileSize];
	memset(szBuffer, 0, dwFileSize);
	if (ReadFile(hFile, szBuffer, dwFileSize, &dwFileSize, NULL))
	{
		return szBuffer;
	}
	else
	{
		return FALSE;
	}
}

DWORD CResTreeDlg::RvaToFoa(DWORD dwRva, char* szBuffer)
{
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
	PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNt);
	if (dwRva < pSectionHeader[0].VirtualAddress)
	{
		return dwRva;
	}
	else
	{
		for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
		{
			if (dwRva >= pSectionHeader[i].VirtualAddress && dwRva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize)
			{
				return dwRva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
			}
		}
	}
	return 0;
}

void CResTreeDlg::OnInitResTree()
{ 
	const WCHAR* g_ResType[0x11] = {
		L"NULL",
		L"鼠标指针",
		L"位图",
		L"图标",
		L"菜单",
		L"对话框",
		L"字符串列表",
		L"字体目录",
		L"字体",
		L"快捷键",
		L"非格式化资源",
		L"消息列表",
		L"鼠标指针组",
		L"NULL",
		L"图标组",
		L"NULL",
		L"版本信息",
	};
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szFileBuffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szFileBuffer + pDos->e_lfanew);
	PIMAGE_OPTIONAL_HEADER pOptionHeader = &pNt->OptionalHeader;
	PIMAGE_DATA_DIRECTORY pResDir = pOptionHeader->DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;

	PIMAGE_RESOURCE_DIRECTORY pFirst = (PIMAGE_RESOURCE_DIRECTORY)(RvaToFoa(pResDir->VirtualAddress, szFileBuffer) + szFileBuffer);
	DWORD dwFirstNum = pFirst->NumberOfIdEntries + pFirst->NumberOfNamedEntries;
	CHAR cRootNameEntries[MAX_PATH];
	sprintf(cRootNameEntries, "%04x", pFirst->NumberOfNamedEntries);
	RootNameEntries = cRootNameEntries;

	CHAR cRootIDEntries[MAX_PATH];
	sprintf(cRootIDEntries, "%04x", pFirst->NumberOfIdEntries);
	RootIDEntries = cRootIDEntries;
	UpdateData(FALSE);

	PIMAGE_RESOURCE_DIRECTORY_ENTRY pFirstEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pFirst + 1);
	HTREEITEM tFirstEntry;
	HTREEITEM tSecondEntry;
	HTREEITEM tResEntry;
	for (DWORD i = 0; i < dwFirstNum; i++)
	{
		if (pFirstEntry[i].NameIsString != 1)
		{
			if (pFirstEntry[i].Id < 0x11)
			{
				tFirstEntry = m_ResTree.InsertItem(g_ResType[pFirstEntry[i].Id], TVI_ROOT, TVI_LAST);
			}
			else
			{				
				WCHAR wcFirstEntryID[MAX_PATH];
				wsprintf(wcFirstEntryID, L"%d", pFirstEntry[i].Id);
				tFirstEntry = m_ResTree.InsertItem(wcFirstEntryID, TVI_ROOT, TVI_LAST);
			}			
		}
		else
		{
			PIMAGE_RESOURCE_DIR_STRING_U pFirstEntryName = (PIMAGE_RESOURCE_DIR_STRING_U)(pFirstEntry[i].NameOffset + (DWORD)pFirst);
			WCHAR wcFirstEntryName[MAX_PATH];
			memset(wcFirstEntryName, 0, MAX_PATH);
			memcpy(wcFirstEntryName, pFirstEntryName->NameString, pFirstEntryName->Length * 2);
			tFirstEntry = m_ResTree.InsertItem(wcFirstEntryName, TVI_ROOT, TVI_LAST);
		}
		if (pFirstEntry[i].DataIsDirectory == 1)
		{
			PIMAGE_RESOURCE_DIRECTORY pSecond = (PIMAGE_RESOURCE_DIRECTORY)(pFirstEntry[i].OffsetToDirectory + (DWORD)pFirst);
			DWORD dwSecondNum = pSecond->NumberOfIdEntries + pSecond->NumberOfNamedEntries;
			for (DWORD i = 0; i < dwSecondNum; i++)
			{
				PIMAGE_RESOURCE_DIRECTORY_ENTRY pSecondEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pSecond + 1);
				if (pSecondEntry[i].NameIsString != 1)
				{
					WCHAR wcSecondEntryID[MAX_PATH];
					wsprintf(wcSecondEntryID, L"%d", pSecondEntry[i].Id);
					tSecondEntry = m_ResTree.InsertItem(wcSecondEntryID, tFirstEntry, TVI_LAST);
				}
				else
				{
					PIMAGE_RESOURCE_DIR_STRING_U pSecondEntryName = (PIMAGE_RESOURCE_DIR_STRING_U)(pSecondEntry[i].NameOffset + (DWORD)pFirst);
					WCHAR wcFirstEntryName[MAX_PATH];
					memset(wcFirstEntryName, 0, MAX_PATH);
					memcpy(wcFirstEntryName, pSecondEntryName->NameString, pSecondEntryName->Length * 2);
					tSecondEntry = m_ResTree.InsertItem(wcFirstEntryName, tFirstEntry, TVI_LAST);
				}
				if (pSecondEntry[i].DataIsDirectory == 1)
				{
					PIMAGE_RESOURCE_DIRECTORY pThird = (PIMAGE_RESOURCE_DIRECTORY)(pSecondEntry[i].OffsetToDirectory + (DWORD)pFirst);
					PIMAGE_RESOURCE_DIRECTORY_ENTRY pThirdEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pThird + 1);
					if (pThirdEntry->DataIsDirectory == 1)
					{
						PIMAGE_RESOURCE_DATA_ENTRY pResDateEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)pFirst + pThirdEntry->OffsetToDirectory);
						
					}
				}
			}
		}
	}
}

由于本人能力有限,并不能全部实现功能,望诸位基于本人相关意见
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

丝杆升降机行程控制:精准运行的奥秘

丝杆升降机作为机械传动领域的 “得力干将”&#xff0c;在环保设备、工业生产线、建筑施工等众多场景中发挥着关键作用。其能够实现重物的升降、平移等操作&#xff0c;而行程控制对于丝杆升降机而言&#xff0c;就如同给机器设定了行动边界&#xff0c;不仅关乎设备能否精准达…

力扣.旋转矩阵Ⅱ

59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 代码区&#xff1a; class Solution {const int MAX25; public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> ans;vector<int> hang;int len_nn;int arry[25][25]…

HFSS 使用入门

资源 下载资源&#xff1a; https://download.csdn.net/download/wangjun_huster/90547193 下载破解&#xff1a; https://download.csdn.net/download/wangjun_huster/90547551 安装 https://www.bilibili.com/list/ml3403866295?oid925751664&bvidBV1CT4y1u7LB 入门…

对内核fork进程中写时复制的理解记录

前言 文章写于学习Redis时对aof后台重写中写时复制的疑问 一、感到不理解的歧义 在部分技术文档中&#xff08;以小林的文章为例&#xff09;&#xff0c;对写时复制后的内存权限存在如歧义&#xff1a; ! 二、正确技术表述 根据Linux内核实现&#xff08;5.15版本&#x…

HarmonyOS-ArkUI Navigation (导航组件)-第一部分

导航组件主要实现页面间以及组件内部的界面跳转&#xff0c;支持在不同的组件间进行参数的传递&#xff0c;提供灵活的跳转栈操作&#xff0c;从而便捷的实现对不同页面的访问和复用。 我们之前学习过Tabs组件&#xff0c;这个组件里面也有支持跳转的方式&#xff0c;Navigati…

【磁盘扩容】linux磁盘扩容

一、新磁盘分区 1、新磁盘在接入服务器后&#xff0c;很好辨认 使用fdisk -l命令&#xff0c;查看&#xff1a; 或者使用 lsblk -f 其中sdb,sdc, sda都是挂载硬盘&#xff0c;sr0为DVD光盘&#xff0c;很明显sdc没有进行任何的挂载&#xff0c;确定sdc为新磁盘 2、格式化新…

详解CountDownLatch底层源码

大家好&#xff0c;我是此林。 今天来分享一下CountDownLatch的底层源码。 CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的线程之间同步工具类&#xff0c;主要用于协调多个线程的执行顺序。其核心思想是通过计数器实现线程间的"等待-唤醒"机制&#…

Python基于EdgeTTS库文本转语音

EdgeTTS&#xff0c;支持粤语等各种方言&#xff0c;无需部署无需Key&#xff0c;完全免费&#xff0c;太香了 因为其底层是使用微软 Edge 的在线语音合成服务&#xff0c;所以不需要下载任何模型&#xff0c;甚至连 api_key 都给你省了&#xff0c;简直不要太良心~ 关键是&a…

MFC案例:利用计时器(Timer)动态绘制正弦曲线

这是一个基于对话框的MFC程序&#xff0c;运行效果是在只画出I、IV象限的坐标系中绘制出红、蓝、绿各相差PI/2的三条正弦曲线&#xff0c;计时器运行一个周期曲线在X轴移动一个像素&#xff08;对应1度&#xff09;&#xff0c;Y轴显示正弦值&#xff08;150个像素代表1&#x…

解析 HTML 网站架构规范

2025/3/28 向全栈工程师迈进&#xff01; 一、网页基本的组成部分 网页的外观多种多样&#xff0c;但是除了全屏视频或游戏&#xff0c;或艺术作品页面&#xff0c;或只是结构不当的页面以外&#xff0c;都倾向于使用类似的标准组件。 1.1页眉 通常横跨于整个页面顶部有一…

Kubernetes》》K8S》》Deployment 、Pod、Rs 、部署 nginx

Deployment deployment文档说明 kubectl get rs,deployment,pods 删除pod 、deployment 、service # 如果只删除pod&#xff0c;deployment会自动重建&#xff0c;所以应该先删除deployment。 # 下面演示的是删除所有deployment&#xff0c;可以指定只删除某个 # 删除所有…

链表(C++)

这是本人第二次学习链表&#xff0c;第一次学习链表是在大一上的C语言课上&#xff0c;首次接触&#xff0c;感到有些难&#xff1b;第二次是在大一下学习数据结构时&#xff08;就是这次&#xff09;&#xff0c;使用C再次理解链表。同时&#xff0c;这也是开启数据结构学习写…

nginx 设置隐藏版本号

Nginx默认会在Server头里包含版本信息&#xff0c;比如“nginx/1.18.0”&#xff0c;这可能存在安全隐患&#xff0c;因为攻击者知道了版本号后&#xff0c;可以针对特定版本的漏洞进行攻击。所以&#xff0c;隐藏版本号是一个常见的安全措施。 可通过在http块里加上server_to…

29_项目

目录 http.js 1、先注册账号 register.html 2、再登录 login.html 3、首页 index.html 4 详情 details.html cart.html css index.css register.css details.css 演示 进阶 http.js let baseURL "http://localhost:8888"; let resgiterApi baseURL &…

排序算法1--插入排序

目录 1.常见排序算法 2.排序算法的预定函数 2.1交换函数 2.2测试算法运行时间的函数 3.插入排序 3.1直接插入排序 3.2希尔排序 3.3插入排序的时间复杂度分析 4.总结 1.常见排序算法 我将分别讲解五种排序算法&#xff0c;但是不代表只有五种固定的代码&#xff0c;之后…

业之峰与宏图智能战略携手,开启家装数字化新篇章

3月8日&#xff0c;业之峰装饰集团董事长张钧携高管团队与宏图智能董事长庭治宏及核心团队&#xff0c;在业之峰总部隆重举行了战略合作签约仪式&#xff0c;标志着双方将携手探索业之峰的数字化转型之路&#xff0c;共同推动家装行业的变革与发展。 近年来&#xff0c;家装行业…

matplotlib标题比x,y轴字体大,明明标题字体更大?

原始代码&#xff1a; plt.xlabel(训练轮次&#xff08;Epochs&#xff09;, fontsize14, fontweightbold, fontpropertieschinese_font) # 设置中文字体、加大、加粗 plt.ylabel(R值, fontsize14, fontweightbold, fontpropertieschinese_font) # 设置中文字体、加大、加粗…

【云原生】docker 搭建单机PostgreSQL操作详解

目录 一、前言 二、前置准备 2.1 服务器环境 2.2 docker环境 三、docker安装PostgreSQL过程 3.1 获取PostgreSQL镜像 3.2 启动容器 3.2.1 创建数据卷目录 3.2.2 启动pg容器 3.3 客户端测试连接数据库 四、创建数据库与授权 4.1 进入PG容器 4.2 PG常用操作命令 4.2…

免费使用!OpenAI 全量开放 GPT-4o 图像生成能力!

2025年3月26日&#xff0c;OpenAI正式推出GPT-4o原生图像生成功能&#xff0c;这一更新不仅标志着多模态AI技术的重大突破&#xff0c;更引发了全球AI厂商的激烈竞争。从免费用户到企业开发者&#xff0c;从创意设计到科学可视化&#xff0c;GPT-4o正在重塑图像生成的边界。本文…

jetson orin nano super AI模型部署之路(三)stable diffusion部署

先看一下部署后的界面和生成的图片。 在jetson orin nano super上部署stable diffusion比较简单&#xff0c;有现成的docker image和代码可用。 docker image拉取 使用的docker image是dustynv/stable-diffusion-webui&#xff0c;对于jetson orin nano super的jetpack6.2来说…