首页
社区
课程
招聘
[原创]PE文件菜单资源的格式分析
2013-6-15 21:55 14662

[原创]PE文件菜单资源的格式分析

2013-6-15 21:55
14662
  前些日子做了一些PE文件分析,查阅了包括《Windows PE 权威指南》在内的不少文章及源码。可能出于实用的需要,大家分析的目标通常集中在区段、输入输出表方面的缘故,很少有文章对资源部分做很详细的介绍,即便是《Windows PE 权威指南》,在菜单资源部分说的也很简练,只分析了第一个简单菜单项,其他的一带而过。为了正确读取菜单资源的内容,不得已查了些资料,做了些分析推测,记录一下。当然,这些分析没有什么实际价值,内容非常初级,并且不带来任何效益,菜鸟以上级别请自行闪退。
  菜单资源的存放实质上是一种顺序储存的多叉树式数据结构,用标志位来记录树节点间的关系。
  
  图1 深度优先存储的多叉树结构

  菜单项有两种:弹出菜单(POPUP)和普通菜单项(MENUITEM)。
弹出菜单项
typedef struct
{
WORD		fItemFlag; //菜单项标志
WCHAR[]	szItemText;//菜单名
}PopupMenuItem;


普通菜单项
typedef struct
{
WORD		fItemFlag; //菜单项标志
WORD		wMenuID;   //菜单ID
WCHAR[]	szItemText;//菜单名
}NormalMenuItem;

  
  fItemFlag是描述菜单项的标志集合,常见标志及对应的值如下(数据来源:WinUser.h):
#define MF_ENABLED          0x00000000L
#define MF_GRAYED           0x00000001L
#define MF_DISABLED         0x00000002L

#define MF_UNCHECKED        0x00000000L
#define MF_CHECKED          0x00000008L
#define MF_USECHECKBITMAPS  0x00000200L

#define MF_STRING           0x00000000L
#define MF_BITMAP           0x00000004L
#define MF_OWNERDRAW        0x00000100L

#define MF_POPUP            0x00000010L
#define MF_MENUBARBREAK     0x00000020L
#define MF_MENUBREAK        0x00000040L
#define MF_END              0x00000080L


  以上这些常量定义是windows惯常使用的方式,以二进制不同的标志位代表不同的含义,通过逻辑运算可以很方便的做出不同的参数设置。搞过Windows编程的,对这种方法早已烂熟于心。简单举例,一个选中的(Checked)、非活动的(Disabled)、分栏断开(MenuBarBreak)的普通菜单项的fItemFlag:
  MF_DISABLED | MF_CHECKED | MF_MENUBARBREAK = 0x0002| 0x0008 | 0x0020 = 0x002A
  以上定义的中文名参考了ResScope中的翻译。你可以用ResScope或者ExeScope很轻松的用修改菜单项,然后用WinHex 或者UltraEdit验证。
  以实例来分析相对会比较简单,因此自己搞了个菜单来分析。

  图2 实验用的菜单资源
  WinHex打开PE文件或者直接在VS里直接读取都没问题。如何定位到菜单资源是PE文件分析中另一层面的内容,同时也是很简单的问题,在此不做分析。图2中的菜单资源Hex视图显示如图3。

图3 资源文件的Hex视图
  从弹出菜单项说起(图3中的红色部分):
  如何才能知道,一个菜单项是弹出菜单还是普通菜单项呢?WinUser.h给出了说明。弹出菜单项的fItemFlag为0x0010,如果弹出菜单项是本层级菜单的最后一个菜单项,则:
  MF_POPUP | MF_END = 0x0010 | 0x0080 = 0x0090,如图3中的菜单项test5。
  同理,菜单项 test3 即使不查看图2,也可知该菜单项为 MF_POPUP | MF_GRAYED,也即一个灰色弹出菜单项。ResScope 查看到的菜单资源样式也是用这种方法分析得来的。
  菜单项的名称为Unicode字符串,长度是不固定的,但无论C语言程序还是Delphi程序,菜单资源中的字符串定义方式都是C方式,字符串的结束符为标准的0x0000。
  
  普通菜单项:
  图3中的蓝色部分为普通菜单项的fItemFlag,0x0080、0x0081之类的一目了然,0x0000的没有设置标志位,为“一般”普通菜单项。绿色部分为每个普通菜单项的ID,和Resourse.h 中的菜单ID一致。
  需要说明的是菜单项test13,资源设置的时候设定了PopUp属性,因此它的资源ID为不可编辑状态,但因为没有给它下一层级的菜单项,程序编译的时候认为它不是一个弹出菜单项,但又没有资源ID,编译器把它的资源ID定义为 -1(0xFFFF)。
  特殊的菜单项 -- 分隔符也是一个普通菜单项,十六进制代码如图3中紫色部分,只是它的fItemFlag、wMenuID均为0,字符串为Unicode的‘\0’,因此分隔符菜单项共占6个字节。
  
  由以上分析可知,每级菜单的结束条件均为 fItemFlag 低八位的最高位为1,可以很简练的用递归方法遍历。这里没有统计菜单的层级,也没有写修改菜单的函数,因为多叉树写起来比较麻烦。
void PEResourse::GetMenu(PBYTE *begin,int level)
{
	WORD fItemFlag = **(WORD **)begin;
	if (level < 0)
		return;
	if (fItemFlag & 0x10)//弹出菜单项
	{
		GetPopMenuItem(begin);
		++ level;
	}
	else
		GetNormalMenuItem(begin);//普通菜单项
	if (fItemFlag & 0x80)
		-- level;
	GetMenu(begin,level);
}

  两个读取菜单项的函数:
void PEResourse::GetNormalMenuItem( PBYTE *begin)//**为移动地址的指针
{
	CString out_str,name_str;
	*begin += sizeof(WORD);
	if (!(**(DWORD **)begin))//分隔符,跳过
		*begin += sizeof(WORD);
	else
	{
		out_str.Format(_T("MENU : %04X\t"),**(WORD **)begin);
		*begin += sizeof(WORD);
		name_str = (PWCHAR)(*begin);
		out_str += name_str;
		m_menu_str.push_back(out_str);	//类成员变量vector <CString> m_menu_str
	}
	*begin += (name_str.GetLength() + 1 )* sizeof(WCHAR);	
}


void PEResourse::GetPopMenuItem( PBYTE *begin)
{
	CString out_str,name_str;
	out_str = _T("POPUP:\t\t");
	*begin += sizeof(WORD);
	name_str = (PWCHAR)(*begin);
	out_str += name_str;
	m_menu_str.push_back(out_str);
	*begin += (name_str.GetLength() + 1 )* sizeof(WCHAR);
}

  
  结语:其实PE文件的资源组织方式都差不多,而且和Windows资源管理器的数据组织方式大同小异,窥一斑可见全豹吧。文中错误之处欢迎指正。
  
参考资料:
1. 《Windows PE 权威指南》
2. Msdn
3. 《C++基于顺序储存的多叉树实现》
4. internet

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞3
打赏
分享
最新回复 (20)
雪    币: 778
活跃值: (208)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
wertyuyuyu 4 2013-6-15 22:04
2
0
mark,以后也许会用到
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
boainfall 2013-6-16 09:18
3
0
这个要顶!!
雪    币: 351
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
白玉箫 2013-6-18 13:57
4
0
写的不错
之前写过pe的东西 资源类的没做细研究
雪    币: 131
活跃值: (303)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
学者learner 2013-6-18 21:01
5
0
这个以后会用到,先看看
雪    币: 102
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
iwantbmw 2013-6-18 22:42
6
0
顶顶顶顶顶顶

学习了,谢谢!
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
asdli 2013-6-18 22:56
7
0
mark  学习了
雪    币: 1412
活跃值: (4204)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
IamHuskar 4 2013-6-19 09:48
8
0
MARKKKKKKKKKKKKKK
雪    币: 434
活跃值: (73)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zyicai 2013-6-19 10:01
9
0
不错的资源
雪    币: 83466
活跃值: (198495)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
linhanshi 2013-6-19 10:19
10
0
Thanks for share.
雪    币: 1379
活跃值: (708)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hmilywen 2013-6-19 17:20
11
0
分析下E语言吧,来个E语言资源编辑工具挺好。
雪    币: 1839
活跃值: (295)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
fosom 8 2013-6-20 10:11
12
0
感谢楼主,我前几年写PELoader的时候,关于资源因为找不到合适的资料,也不是很重要就放弃了。

所以感谢楼主,得空把我的PELoader的资源部分也可以完善一下了。
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
MONKEYiiD 2013-6-20 17:43
13
0
不错,学习了~
雪    币: 483
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
俟我 2013-6-21 00:05
14
0
受教了
雪    币: 327
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
shwsf 1 2013-6-21 23:24
15
0
非常不错!学习了
雪    币: 14
活跃值: (78)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
乐乐侠 2013-6-24 18:30
16
0
支持一下,留个名吧
雪    币: 14
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jxrbwwp 2013-7-27 16:19
17
0
刚入论坛不久,潜心学习
雪    币: 216
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lupming 2013-8-4 14:50
18
0
make下,已经说不定需要
雪    币: 253
活跃值: (27)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
呱叽呱叽 1 2013-8-4 17:36
19
0
可以参考这本书
[Windows.PE权威指南].戚利.扫描版.pdf
雪    币: 11
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
七天chn 2013-8-5 09:15
20
0
mark mark!
雪    币: 9
活跃值: (130)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
yber 2018-1-12 12:26
21
0
多叉数
游客
登录 | 注册 方可回帖
返回