该宏的作用是:用结构成员中的ListEntry的地址(0x108)减去结构体成员ListEntry到首地址的距离(8)得到结构体首地址(0x100)
pEntry 中的值就是结构成员中的ListEntry的地址。即:0x108 | ListEntry | 0x108 结构成员中的ListEntry的地址,也是RemoveHeadList函数函数的值 |
| y | 0x104 |
ListEntry在结构体中的开始位置是8 | x | 0x100 结构体首地址 |
| 第一个成员存在低地址上 | 内存的地址是由低往高 |
typedef struct _MYDATASTRUCT{
Ulongx;
Ulongy;
LIST_ENTRYListEntry;//这里的ListEntry在结构体中的开始位置是8
}MYDATASTRUCT,*PMYDATASTRUCT;
PLIST_ENTRYpEntry = RemoveHeadList(&head);
PMYDATASTRUCTpMydata = CONTAINING_RECORD(pEntry,MYDATASTRUCT,ListEntry);//可以理解为:pEntry - &(MYDATASTRUCT.ListEntry)
【注释】
1、实参MYDATASTRUCT和ListEntry用于返回ListEntry在结构体中的开始位置(8),之后用结构成员中的ListEntry的地址(保存在了pEntry中)减去它就得到了结构体首地址(0x100)
2、反汇编代码如下:
#define CONTAINING_RECORD(address, type, field) ((type*)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
注释:
(type *)((PCHAR)(address) 就是pEntry中的值,就是0x108
(ULONG_PTR)(&((type *)0)->field))) 返回的是结构体成员ListEntry到首地址的距离
((type *)0)->field 转换为(*0).field = [EAX(eax=0) + field] 。& 会转换为LEA就返回8了 即:LEA [EAX(0)+8]
即:0x108- 8 = 0x100 指向了结构体的起始地址
3、若MYDATASTRUCT结构的第一个成员是ListEntry,就不存在这种问题了,因为100-0=100还是指向了结构体的第一个成员, CONTAINING_RECORD的作用就是指向结构体的第一个成员,若不指向结构体第一个成员就该报错了。
typedef struct _MYDATASTRUCT{
LIST_ENTRY ListEntry;
ULONG x;
Ulong y;
}MYDATASTRUCT,*PMYDATASTRUCT;
|
|
|
| y | 0x108 |
| x | 0x104 |
pEntry 起初指向了这里--> | ListEntry | 0x100 |