PE文件解析-资源中的对话框结构

一、概述

    想要获取一个可执行文件(PE文件)里包含的资源文件,首先要解析可执行文件,得到资源存储的地址及大小,可参考 https://blog.csdn.net/zhyulo/article/details/85717711 。然后,根据资源存储方式,得到各资源的数据内容及其大小,可参考 https://blog.csdn.net/zhyulo/article/details/85930045 。

    PE文件的资源中,对话框的资源类型ID=5。对话框不仅可以通过ID区别,也可以通过对话框名区别,也就是说对话框在资源树的第二层可能是数字,也可能是字符串,这点需要注意。

    对话框可以说是资源中最重要、最复杂的一种。它在结构上分为2种:基本对话框和扩展对话框。而由于对于一些属性,既可能是字符串ID,又可能直接就是字符串,所以在这里又把它们的存储设计了一种新的方式。而由于对话框上有不同控件,不同控件所需的参数又不尽相同,所以解析对话框在资源中的数据流一直是一个难点。在博主的不断的努力下,才揭开了它的神秘面纱。

    在RC文件中,基本对话框和扩展对话框的定义如下所示:

IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 235, 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "关于 test"
FONT 9, "宋体"
BEGIN
    ICON            IDR_MAINFRAME,IDC_STATIC,11,17,20,20
    LTEXT           "test 1.0 版",IDC_STATIC,40,10,119,8,
                    SS_NOPREFIX
    LTEXT           "版权所有 (C) 2019",IDC_STATIC,40,25,119,8
    DEFPUSHBUTTON   "确定",IDOK,178,7, 50,14,WS_GROUP
END

IDD_TEST_DIALOG DIALOGEX  0, 0, 320, 200 
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "test"
FONT 9, "宋体"
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,260,7,50,14
    PUSHBUTTON      "取消",IDCANCEL,260,23,50,14
    LTEXT           "TODO: 在这里设置对话控制。",IDC_STATIC,50,90,200,8
END

二、ID与字符串组合结构

    由于对于一些属性,既可能是字符串ID,又可能直接就是字符串,所以在这里又把它们的存储设计了一种新的方式。注意,这里的字符串都是Unicode字符串。如果该结构的第一个字是0xFFFF,说明该结构是一个ID,ID值为第二个字。否则,该结构是一个字符串,以'\0'结束。我们这里把这个结构定义为IDStr。

三、基本对话框的资源结构

    基本对话框包括对话框头和子控件2部分组成。对话框头与每一个子控件4字节对齐。

1.对话框头

    对话框头的结构如下所示:

struct DialogBoxHeader
{
	DWORD Style;//标准窗口样式
	DWORD ExStyle;//扩展窗口样式
	WORD DlgItems;//控件数量
	WORD x;//起点坐标
	WORD y;
	WORD cx;//宽
	WORD cy;//高
	IDStr menuName;//菜单名
	IDStr ClassName;//类名
	IDStr szCaption;//对话框标题
};

    如果Style的DS_SETFONT位为1,那么在DialogBoxHeader的结构后还有字体结构,字体结构如下:

struct DialogFont
{
	WORD wPointSize;//字体大小
	WCHAR FontName[1];
};

2.子控件

    基本对话框的子控件结构如下:

struct ControlData
{
	DWORD Style;//标准窗口样式
	DWORD ExStyle;//扩展窗口样式
	WORD x;//左上角坐标
	WORD y;
	WORD cx;//长
	WORD cy;//高
	WORD id;//控件ID
	IDStr ClassID;//控件类型
	IDStr Text;//控件名称
	WORD ExtraStuff;
};

四、扩展对话框的资源结构

    与基本对话框一样,扩展对话框也由扩展对话框头和子控件2部分组成。扩展对话框头与每一个子控件4字节对齐。

1.扩展对话框头

    扩展对话框头的结构如下所示:

struct DialogBoxHeaderEx
{
	DWORD SignEx;//0xFFFF0001
	DWORD Version;
	DWORD ExStyle;//扩展窗口样式
	DWORD Style;//标准窗口样式
	WORD DlgItems;//控件数量
	WORD x;//起点坐标
	WORD y;
	WORD cx;//宽
	WORD cy;//高
	IDStr menuName;//菜单名
	IDStr ClassName;//类名
	IDStr szCaption;//对话框标题
};

    如果Style的DS_SETFONT位为1,那么在DialogBoxHeaderEx的结构后还有字体结构,字体结构如下:

struct DialogFontEx
{
	WORD wPointSize;//字体大小
	WORD Weight;
	BYTE Italic;
	BYTE CharSet;
	WCHAR FontName[1];
};

2.子控件

    扩展对话框的子控件结构如下:

struct ControlDataEx
{
	DWORD helpID;//帮助ID
	DWORD ExStyle;//扩展窗口样式
	DWORD Style;//标准窗口样式
	WORD x;//左上角坐标
	WORD y;
	WORD cx;//长
	WORD cy;//高
	WORD id;//控件ID
	WORD ;
	IDStr ClassID;//控件类型
	IDStr Text;//控件名称
	WORD ExtraStuffLen;
	BYTE ExtraBuf[1];
};

五、实例代码

    以下程序展示了如何从PE文件的对话框资源流中读取并输出成RC文件形式的代码。

char* GetDialogItem(char *buf, bool isExtra)
{
	DWORD Style, ExStyle, NotStyle;
	struct ControlData//标准对话框组件结构
	{
		DWORD Style;//标准窗口样式
		DWORD ExStyle;//扩展窗口样式
		WORD x;//左上角坐标
		WORD y;
		WORD cx;//长
		WORD cy;//高
		WORD id;//控件ID
	} *Item;
	DWORD *helpID = NULL;//只有扩展对话框才有帮助ID
	WCHAR *ClassID, *Text, *ExtraStuff;
	if(isExtra)
	{
		helpID = (DWORD*)buf;
		Item = (ControlData*)(helpID + 1);
	}
	else Item = (ControlData*)buf;
	Style = isExtra ? Item->ExStyle : Item->Style;
	ExStyle = isExtra ? Item->Style : Item->ExStyle;

	Puts("\t");
	//控件类型
	ClassID = (WCHAR*)&Item->id + 1;
	if(isExtra) ClassID++;
	const char *pStyle[] = {"", "button", "static"};
	int isControl = 0;
	if(*ClassID == 0xFFFF)
	{
		Text = ClassID + 2;
		switch(ClassID[1])
		{
		case 0x0080:
			switch(Style & 0x0FL)
			{
			case BS_PUSHBUTTON: Puts("PUSHBUTTON"); NotStyle = BS_PUSHBUTTON | WS_TABSTOP; break;
			case BS_DEFPUSHBUTTON: Puts("DEFPUSHBUTTON"); NotStyle = BS_DEFPUSHBUTTON | WS_TABSTOP; break;
			case BS_CHECKBOX: Puts("CHECKBOX"); NotStyle = BS_CHECKBOX | WS_TABSTOP; break;
			case BS_AUTOCHECKBOX: Puts("AUTOCHECKBOX"); NotStyle = BS_AUTOCHECKBOX; break;
			case BS_RADIOBUTTON: Puts("RADIOBUTTON"); NotStyle = BS_RADIOBUTTON; break;
			case BS_3STATE: Puts("STATE3\t"); NotStyle = BS_3STATE; break;
			case BS_AUTO3STATE: Puts("AUTO3STATE"); NotStyle = BS_AUTO3STATE; break;
			case BS_GROUPBOX: Puts("GROUPBOX"); NotStyle = BS_GROUPBOX; break;
			case BS_AUTORADIOBUTTON: Puts("AUTORADIOBUTTON"); NotStyle = BS_AUTORADIOBUTTON; break;
			default: Puts("CONTROL\t"); NotStyle = 0; isControl = 1;
			}
			break;
		case 0x0081: Puts("EDITTEXT"); NotStyle = ES_LEFT | WS_BORDER | WS_TABSTOP; break;
		case 0x0082:
			switch(Style & 0x0FL)
			{
			case SS_LEFT: Puts("LTEXT\t"); NotStyle = SS_LEFT | WS_GROUP; break;
			case SS_RIGHT: Puts("RTEXT\t"); NotStyle = SS_RIGHT | WS_GROUP; break;
			case SS_CENTER: Puts("CTEXT\t"); NotStyle = SS_CENTER | WS_GROUP; break;
			case SS_ICON: Puts("ICON\t"); NotStyle = SS_ICON; break;
			default: Puts("CONTROL\t"); NotStyle = 0; isControl = 2;
			}
			break;
		case 0x0083: Puts("LISTBOX\t"); NotStyle = WS_BORDER | LBS_NOTIFY; break;
		case 0x0084: Puts("SCROLLBAR"); NotStyle = 0; break;
		case 0x0085: Puts("COMBOBOX"); NotStyle = 0; break;
		default: Puts("CONTROL\t"); NotStyle = 0; isControl = -2;
		}
	}
	else
	{
		Text = ClassID;
		while(*Text) Text++;
		Text++;
		Puts("CONTROL\t");
		NotStyle = 0;
		isControl = -1;
	}
	Puts("\t");
	NotStyle |= WS_CHILD | WS_VISIBLE;
	Style &= ~NotStyle;
	NotStyle &= ~(isExtra ? Item->ExStyle : Item->Style);
	//控件名称
	if(*Text == 0xFFFF)
	{
		PutIDName(Text[1]);//控件ID
		Puts(", ");
		ExtraStuff = Text + 2;
	}
	else
	{
		if(!isControl && (ClassID[1] == 0x0081 || ClassID[1] == 0x0083//EDITTEXT,LISTBOX无需此项
			|| ClassID[1] == 0x0084 || ClassID[1] == 0x0085))
		{
			ExtraStuff = Text;
			while(*ExtraStuff) ExtraStuff++;
			ExtraStuff++;
		}
		else
		{
			Puts("\"");
			ExtraStuff = (WCHAR*)WPuts(Text);
			Puts("\", ");
		}
	}
	//控件ID
	PutIDName(Item->id, "IDC_");
	//扩展控件类型
	if(isControl)
	{
		Puts(", ");
		if(isControl == -1)
		{
			Puts("\"");
			WPuts(ClassID);
			Puts("\"");
		}
		else if(isControl > 0) Puts(pStyle[isControl]);
		else Print("%d", ClassID[1]);
		//标准窗口样式
		if(Style || !NotStyle) Print(", %#x", Style);
		if(NotStyle)
		{
			if(Style) Puts(" |");
			else Puts(",");
			Print(" NOT %#x", NotStyle);
		}
		Style = NotStyle = 0;
	}

	Print(", %d,%d,%d,%d",Item->x,Item->y,Item->cx,Item->cy);
	if(Style || NotStyle || ExStyle || (isExtra && *helpID))
	{
		if(Style || (!NotStyle && !isControl)) Print(", %#x", Style);
		if(NotStyle)
		{
			if(Style) Puts(" |");
			else Puts(",");
			Print(" NOT %#x", NotStyle);
		}
		if(ExStyle || (isExtra && *helpID))
		{
			if(ExStyle == WS_EX_STATICEDGE)
				Puts(", WS_EX_STATICEDGE");
			else Print(", %#x", ExStyle);
			if(isExtra && *helpID)
			{
				Puts(", ");
				PutIDName(Item->id, "HIDC_");
			}
		}
	}
	Puts("\n");
	buf = (char*)(ExtraStuff + 1);
	if(isExtra) buf += *ExtraStuff;
	return buf;
}

void GetDialog(char *buf, CStrID id)
{
	if(*(DWORD*)buf == 0xFFFF0001)
	{
		GetDialogEx(buf, id);
		return;
	}
	if(id.IsID()) PutIDName(id.GetID(), "IDD_DIALOG");
	else Puts(id.GetStr());
	Puts(" DIALOG");

	struct DialogBoxHeader
	{
		DWORD Style;//标准窗口样式
		DWORD ExStyle;//扩展窗口样式
		WORD DlgItems;//控件数量
		WORD x;//起点坐标
		WORD y;
		WORD cx;//宽
		WORD cy;//高
		WCHAR menuName[1];//菜单名
	} *Header = (DialogBoxHeader*)buf;
	Print(" DISCARDABLE %d, %d, %d, %d\n",//对话框坐标
		Header->x,Header->y,Header->cx,Header->cy);
	Print("STYLE 0x%.8X\n", Header->Style & (~DS_SETFONT));//标准窗口样式
	if(Header->ExStyle) Print("EXSTYLE 0x%.8X\n", Header->ExStyle);//扩展窗口样式
	WCHAR *ClassName,*szCaption;
	char *ItemData;
	//菜单名
	if(Header->menuName[0] == 0xFFFF)
	{
		Print("MENU %d\n",Header->menuName[1]);
		ClassName = Header->menuName + 2;
	}
	else if(Header->menuName[0])
	{
		Puts("MENU \"");
		ClassName = (WCHAR*)WPuts(Header->menuName);
		Puts("\"\n");
	}
	else ClassName = Header->menuName + 1;
	//类名
	if(*ClassName == 0xFFFF)
	{
		Print("CLASS %d\n",ClassName[1]);
		szCaption = ClassName + 2;
	}
	else if(*ClassName)
	{
		Puts("CLASS \"");
		szCaption = (WCHAR*)WPuts(ClassName);
		Puts("\"\n");
	}
	else szCaption = ClassName + 1;
	//对话框标题
	if(*szCaption)
	{
		Puts("CAPTION \"");
		ItemData = (char*)WPuts(szCaption);
		Puts("\"\n");
	}
	else ItemData = (char*)(szCaption + 1);
	if(Header->Style & DS_SETFONT)//字体结构
	{
		struct DialogFont
		{
			WORD wPointSize;//字体大小
			WCHAR FontName[1];
		} *Font = (DialogFont*)ItemData;
		Print("FONT %d, \"", Font->wPointSize);
		ItemData = (char*)WPuts(Font->FontName);
		Puts("\"\n");
	}

	Puts("BEGIN\n");
	for(int i=0; i<Header->DlgItems; i++)
	{
		ItemData = (ItemData - buf + 3) /4*4 + buf;
		ItemData = GetDialogItem(ItemData, false);
	}
	Puts("END\n");
}

void GetDialogEx(char *buf, CStrID id)
{
	if(id.IsID()) PutIDName(id.GetID(), "IDD_DIALOG");
	else Puts(id.GetStr());
	Puts(" DIALOGEX");

	struct DialogBoxHeaderEx
	{
		DWORD SignEx;//0xFFFF0001
		DWORD Version;
		DWORD ExStyle;//扩展窗口样式
		DWORD Style;//标准窗口样式
		WORD DlgItems;//控件数量
		WORD x;//起点坐标
		WORD y;
		WORD cx;//宽
		WORD cy;//高
		WCHAR menuName[1];//菜单名
	} *Header = (DialogBoxHeaderEx*)buf;
	Print(" DISCARDABLE %d, %d, %d, %d\n",//对话框坐标
		Header->x,Header->y,Header->cx,Header->cy);
	Print("STYLE 0x%.8X\n", Header->Style & (~DS_SETFONT));//标准窗口样式
	if(Header->ExStyle) Print("EXSTYLE 0x%.8X\n", Header->ExStyle);//扩展窗口样式
	WCHAR *ClassName,*szCaption;
	char *ItemData;
	//菜单名
	if(Header->menuName[0] == 0xFFFF)
	{
		Print("MENU %d\n",Header->menuName[1]);
		ClassName = Header->menuName + 2;
	}
	else if(Header->menuName[0])
	{
		Puts("MENU \"");
		ClassName = (WCHAR*)WPuts(Header->menuName);
		Puts("\"\n");
	}
	else ClassName = Header->menuName + 1;
	//类名
	if(*ClassName == 0xFFFF)
	{
		Print("CLASS %d\n",ClassName[1]);
		szCaption = ClassName + 2;
	}
	else if(*ClassName)
	{
		Puts("CLASS \"");
		szCaption = (WCHAR*)WPuts(ClassName);
		Puts("\"\n");
	}
	else szCaption = ClassName + 1;
	//对话框标题
	if(*szCaption)
	{
		Puts("CAPTION \"");
		ItemData = (char*)WPuts(szCaption);
		Puts("\"\n");
	}
	else ItemData = (char*)(szCaption + 1);
	if(Header->Style & DS_SETFONT)//字体结构
	{
		struct DialogFontEx
		{
			WORD wPointSize;//字体大小
			WORD Weight;
			BYTE Italic;
			BYTE CharSet;
			WCHAR FontName[1];
		} *Font = (DialogFontEx*)ItemData;
		Print("FONT %d, \"", Font->wPointSize);
		ItemData = (char*)WPuts(Font->FontName);
		Print("\", %d, %d, %d\n", Font->Weight, Font->Italic, Font->CharSet);
	}

	Puts("BEGIN\n");
	for(int i=0; i<Header->DlgItems; i++)
	{
		ItemData = (ItemData - buf + 3) /4*4 + buf;
		ItemData = GetDialogItem(ItemData, true);
	}
	Puts("END\n");
}

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页