I'm currently trying to wrap my head around some Windows lookaside lists, and I'm seeing some memory addresses that are confusing me.
From another question I posted, some code was produced, thanks to sergmat (original question):
lkd> !lookaside iopsmallirplookasidelist
Lookaside "" @ 82d5ffc0 "Irps"
....
lkd> dt _SINGLE_LIST_ENTRY 82d5ffc0
nt!_SINGLE_LIST_ENTRY
+0x000 Next : 0x86737e30 _SINGLE_LIST_ENTRY
....
lkd> !pool 0x86737e30
Pool page 86737e30 region is Nonpaged pool
*86737e28 size: a0 previous size: 48 (Allocated) *Irp
Pooltag Irp : Io, IRP packets
Essentially what can be seen from the WinDBG output above, is that there is a singly linked list at address 0x82d5ffc0. This output was generated on a 32-bit Windows 7 system.
However, and this is where I get confused, when performing the same operation on a Windows 7 64-bit system, this is the output (addresses are obviously different):
lkd> !lookaside iopsmallirplookasidelist
Lookaside "" @ fffff80002a14800 "Irps"
....
lkd> dt _SINGLE_LIST_ENTRY fffff80002a14800
ntdll!_SINGLE_LIST_ENTRY
+0x000 Next 0x00000000'01bf0003
....
!pool 0x0000000001bf0003
Pool page 000000001bf0003 region is unknown
...
It would appear that the Next
value of 0x0000000001bf0003
isn't a valid virtual address, and I've also tried performing a virtual-to-physical translation on it, which fails.
It looks like this value is some kind of offset into a page, but I'm not entirely sure how the address should be calculated.
There is additional data within the list header, an _SLIST_HEADER
structure, which preceeds the _SINGLE_LIST_ENTRY
. It contains the following data:
Alignment: 0x1bf0003
Region: 0xfffffa8001df5b01
After the initial header is a series of three unions, and as this is a 64-bit system, I believe the Header16
union should be used, which contains this:
Depth: 0x3
Sequence: 0x1bf
HeaderType: 0x1
Init: 0x0
Reserved: 0x0
NextEntry: 0xfffffa8001df5b0
The Header16.NextEntry
element does contain a valid virtual address, so I'm not sure if this is the actual value of the next list element, or something else.
So if anyone could help to clarify how the _SINGLE_LIST_ENTRY.Next
element is calculated on 64-bit systems, I'd greatly appreciate it.
Thanks
The SLIST_HEADER is documented in the WDK:
typedef union DECLSPEC_ALIGN(16) _SLIST_HEADER {
struct { // original struct
ULONGLONG Alignment;
ULONGLONG Region;
} DUMMYSTRUCTNAME;
struct { // 8-byte header
ULONGLONG Depth:16;
ULONGLONG Sequence:9;
ULONGLONG NextEntry:39;
ULONGLONG HeaderType:1; // 0: 8-byte; 1: 16-byte
ULONGLONG Init:1; // 0: uninitialized; 1: initialized
ULONGLONG Reserved:59;
ULONGLONG Region:3;
} Header8;
struct { // ia64 16-byte header
ULONGLONG Depth:16;
ULONGLONG Sequence:48;
ULONGLONG HeaderType:1; // 0: 8-byte; 1: 16-byte
ULONGLONG Init:1; // 0: uninitialized; 1: initialized
ULONGLONG Reserved:2;
ULONGLONG NextEntry:60; // last 4 bits are always 0's
} Header16;
struct { // x64 16-byte header
ULONGLONG Depth:16;
ULONGLONG Sequence:48;
ULONGLONG HeaderType:1; // 0: 8-byte; 1: 16-byte
ULONGLONG Reserved:3;
ULONGLONG NextEntry:60; // last 4 bits are always 0's
} HeaderX64;
} SLIST_HEADER, *PSLIST_HEADER;
So you want the HeaderX64. In addition, the NextEntry address is only 60 bits in the structure and, per the comment, the last four bits are always zero. So, here's an example from my system (cleaned up a bit):
1: kd> dt nt!_SLIST_HEADER 0xfffff80001862800 HeaderX64.
+0x000 HeaderX64 :
+0x000 Depth : (0x2)
+0x000 Sequence : (0x58)
+0x008 HeaderType : 0x1
+0x008 Reserved : 0x0
+0x008 NextEntry : (0xfffffa80038556b)
Adding a zeroed nibble to the end:
1: kd> !pool 0xfffffa80038556b0 2
Pool page fffffa80038556b0 region is Nonpaged pool
*fffffa80038556a0 size: 130 previous size: 80 (Allocated) *Irp
Pooltag Irp : Io, IRP packets