定位资源表
资源表位于PE结构中的数据目录表的第三项,下标为2
资源表总览
可以看到,资源表分为三层,每一个资源节点由两部分组成。最上面的绿色部分是一个资源目录:
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics; //资源属性 保留 0
DWORD TimeDateStamp; //资源创建的时间
WORD MajorVersion; //资源版本号 未使用 0
WORD MinorVersion; //资源版本号 未使用 0
WORD NumberOfNamedEntries; //以名称命名的资源数量
WORD NumberOfIdEntries; //以ID命名的资源数量
// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
其中,NumberOfNamedEntries + NumberOfIdEntries == 资源节点里面黄色部分的数量
而每个黄色部分就是一个资源目录项:
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union { //目录项的名称、或者ID
struct {
DWORD NameOffset:31;
DWORD NameIsString:1;
};
DWORD Name;
WORD Id;
};
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
};
};
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY
其中,“ :” 表示位域,即该成员占多少个二进制位
也就是说,NameOffset占低31位,NameIsString占最高的1位,两个成员共同占据4字节空间
而这个Name成员在每一层中的含义也不一样:
在第一层中,Name代表了资源类型,从1到16,一共16种系统已经写好的预定义类型
而在这层,每个资源目录项的第一个union中存储的内容需要判断其最高位(也就是NameIsString的值)为0还是1来决定
若这个最高位的值为1,则代表这个union的低31位是一个指针,指向了一个结构:
typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
WORD Length; //Unicode字符串的长度
WCHAR NameString[ 1 ]; //Unicode字符串的起始位置
} IMAGE_RESOURCE_DIR_STRING_U,
*PIMAGE_RESOURCE_DIR_STRING_U;
若这个最高位的值为0,则代表这个union的低31位是一个数字,作ID使用
判断方法:
printf("%x\n",(pResourceEntry[i].Name & 0x80000000) == 0x80000000); //位运算
printf("%x\n",pResourceEntry[i].NameIsString == 1); //直接比较NameIsString成员的值
第二个union的成员OffsetToData指向的则是下一层的相应资源节点的地址。我们可以发现第二个union同样也将OffsetToData分成了最高位和低31位
其中,最高位如果为1,低31位 + 资源地址 == 下一层目录节点的起始位置
而第一层、第二层的最高位全为1
最高位如果为0,低31位就指向结构体IMAGE_RESOURCE_DATA_ENTRY:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
但是,OffsetToData并不是一个RVA:
首地址 + 资源表的FOA + OffsetToData的低31位 == 下一层的相应资源节点的真正地址(首地址 + 资源表的FOA == 资源表的首地址)
第三层为0,也就是说,第三层的OffsetToData通过上面加粗的公式得到的地址是结构体IMAGE_RESOURCE_DATA_ENTRY的真正地址
第二层的Name代表的是资源编号
第三层的Name代表的是代码页