Home > Articles > Operating Systems, Server > Microsoft Servers

  • Print
  • + Share This

Windows 2000 Memory Internals

Along with the global separation of the 4-GB address space into user-mode and kernel-mode portions, these two halves are subdivided into various smaller blocks. As you might have guessed, most of them contain undocumented structures that serve undocumented purposes. It would be easy to forget about them if they were uninteresting. However, that's not the case—some of them are a real gold mine for anyone developing system diagnosis or debugging software.

Basic Operating System Information

Now the time has come to introduce one of the postponed command line options of the memory spy application w2k_mem.exe. If you take a look at the lower half of the program's help screen in Example 4-1, you will see a section titled "System Status Options." Let's try the option +o, named "display OS information." Example 4-12 shows a sample run on my machine. The data displayed here are the contents of the SPY_OS_INFO structure, defined in Listing 4-13 and set up by the spy device function SpyOutputOsInfo(), also included in Listing 4-13. In Example 4-12, you can already see some characteristic addresses within the 4-GB linear memory space of a process. For example, the valid user address range is reported to be 0x00010000 to 0x7FFEFFFF. You have probably read in other programming books about Windows NT or Windows 2000 that the first and last 64 KB of the user-mode half of linear memory are "no-access regions" that are there to catch wild pointers produced by common programming errors (cf. Solomon 1998, Chapter 5). The output of w2k_mem.exe proves that this is correct.

Example 4-12. Displaying Operating System Information

E:\>w2k_mem +o
[...]
OS information:
---------------

Memory page size         : 4096 bytes
Memory page shift        : 12 bits
Memory PTI  shift        : 12 bits
Memory PDI  shift        : 22 bits
Memory page mask         : 0xFFFFF000
Memory PTI  mask         : 0x003FF000
Memory PDI  mask         : 0xFFC00000
Memory PTE  array        : 0xC0000000
Memory PDE  array        : 0xC0300000

Lowest user address      : 0x00010000
Thread environment block : 0x7FFDE000
Highest user address     : 0x7FFEFFFF
User probe address       : 0x7FFF0000
System range start       : 0x80000000
Lowest system address    : 0xC0800000
Shared user data         : 0xFFDF0000
Processor control region : 0xFFDFF000
Processor control block  : 0xFFDFF120

Global flag              : 0x00000000
i386 machine type        : 0
Number of processors     : 1
Product type             : Windows NT Workstation (1)
Version & Build number   : 5.00.2195
System root              : "E:\WINNT"
[...]

The last three lines of Example 4-12 contain interesting information about the system, mostly extracted from the SharedUserData area at address 0xFFDF0000. The data structure maintained there by the system is called KUSER_SHARED_DATA and is defined in the DDK header file ntddk.h.

Windows 2000 Segments and Descriptors

Another fine option of w2k_mem.exe is +c, which displays and interprets the contents of the processor's segment registers and descriptor tables. Example 4-13 shows the typical output. The contents of the CS, DS, and ES segment registers clearly demonstrate that Windows 2000 provides each process with a flat 4-GB address space: These basic segments start at offset 0x00000000 and have a limit of 0xFFFFFFFF.

The flag characters in the rightmost column indicate the segment type as defined by its descriptor's Type member. The type attributes of code and data segments are symbolized by combinations of the characters "cra" and "ewa," respectively. A dash means that the corresponding attribute is not set. A Task State Segment (TSS) can have the attributes "a" (available) and "b" (busy) only. All applicable attributes are summarized in Table 4-5. Example 4-13 shows that the Windows 2000 CS segments are nonconforming and allow execute/read access, whereas the DS, ES, FS, and SS segments are of expand-up type and allow read/write access. Another inconspicuous but important detail is the different DPL of the CS, FS, and SS segments in user- and kernel-mode. DPL is the Descriptor Privilege Level. For nonconforming code segments, the DPL specifies the privilege level a caller must be on in order to be able to call into this segment (cf. Intel 1999c, pp. 4-8f). In user-mode, the required level is three; in kernel-mode, it is zero. For data segments, the DPL is the lowest privilege level required to be able to access the segment. This means that the FS and SS segments are accessible from all privilege levels in user-mode, whereas only level-0 accesses are allowed in kernel-mode.

Example 4-13. Displaying CPU Information

E:\>w2k_mem +c
[...]
CPU information:
----------------

User mode segments:

CS  : Selector = 001B, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = CODE -ra
DS  : Selector = 0023, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = DATA -wa
ES  : Selector = 0023, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = DATA -wa
FS  : Selector = 0038, Base = 7FFDE000, Limit = 00000FFF, DPL3, Type = DATA -wa
SS  : Selector = 0023, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = DATA -wa
TSS : Selector = 0028, Base = 80244000, Limit = 000020AB, DPL0, Type = TSS32 b

Kernel mode segments:

CS  : Selector = 0008, Base = 00000000, Limit = FFFFFFFF, DPL0, Type = CODE -ra
DS  : Selector = 0023, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = DATA -wa
ES  : Selector = 0023, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = DATA -wa
FS  : Selector = 0030, Base = FFDFF000, Limit = 00001FFF, DPL0, Type = DATA -wa
SS  : Selector = 0010, Base = 00000000, Limit = FFFFFFFF, DPL0, Type = DATA -wa
TSS : Selector = 0028, Base = 80244000, Limit = 000020AB, DPL0, Type = TSS32 b

IDT : Limit    = 07FF, Base = 80036400
GDT : Limit    = 03FF, Base = 80036000
LDT : Selector = 0000

CR0 : Contents = 8001003B
CR2 : Contents = 00401050
CR3 : Contents = 06F70000
[...]

The contents of the IDT and GDT registers show that the GDT spans from linear address 0x80036000 to 800363FF, immediately followed by the IDT, occupying the address range 0x80036400 to 0x80036BFF. With each descriptor taking 64 bits, the GDT and IDT contain 128 and 256 entries, respectively. Note that the GDT could comprise as many as 8,192 entries, but Windows 2000 uses only a small fraction of them.

The w2k_mem.exe utility features two more options—+g and +i—that display more details about the GDT and IDT. Example 4-14 demonstrates the output of the +g option. It is similar to the "kernel-mode segments:" section of Example 4-13, but lists all segment selectors available in kernel-mode, not just those that are stored in segment registers. w2k_mem.exe compiles this list by looping through the entire GDT,

Table 4-5. Code and Data Segment Type Attributes

SEGMENT

ATTRIBUTE

DESCRIPTION

CODE

c

Conforming segment (may be entered by less privileged code)

CODE

r

Read-access allowed (as opposed to execute-only access)

CODE

a

Segment has been accessed

DATA

e

Expand-down segment (typical attribute for stack segments)

DATA

w

Write-access allowed (as opposed to read-only access)

DATA

a

Segment has been accessed

TSS32

a

Task State Segment is available

TSS32

b

Task State Segment is busy


querying the spy device for segment information by means of the IOCTL function SPY_IO_SEGMENT. Only valid selectors are displayed. It is interesting to compare Examples 4-13 and 4-14 with the GDT selector definitions in ntddk.h, summarized in Table 4-6. Obviously, they are in accordance with the details reported by w2k_mem.exe.

Example 4-14. Displaying GDT Descriptors

E:\>w2k_mem +g
[...]
GDT information:
----------------

001 : Selector = 0008, Base = 00000000, Limit = FFFFFFFF, DPL0, Type = CODE -ra
002 : Selector = 0010, Base = 00000000, Limit = FFFFFFFF, DPL0, Type = DATA -wa
003 : Selector = 0018, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = CODE -ra
004 : Selector = 0020, Base = 00000000, Limit = FFFFFFFF, DPL3, Type = DATA -wa
005 : Selector = 0028, Base = 80244000, Limit = 000020AB, DPL0, Type = TSS32 b
006 : Selector = 0030, Base = FFDFF000, Limit = 00001FFF, DPL0, Type = DATA -wa
007 : Selector = 0038, Base = 7FFDE000, Limit = 00000FFF, DPL3, Type = DATA -wa
008 : Selector = 0040, Base = 00000400, Limit = 0000FFFF, DPL3, Type = DATA -wa
009 : Selector = 0048, Base = E2E6A000, Limit = 00000177, DPL0, Type = LDT
00A : Selector = 0050, Base = 80470040, Limit = 00000068, DPL0, Type = TSS32 a
00B : Selector = 0058, Base = 804700A8, Limit = 00000068, DPL0, Type = TSS32 a
00C : Selector = 0060, Base = 00022AB0, Limit = 0000FFFF, DPL0, Type = DATA -wa
00D : Selector = 0068, Base = 000B8000, Limit = 00003FFF, DPL0, Type = DATA -w-
00E : Selector = 0070, Base = FFFF7000, Limit = 000003FF, DPL0, Type = DATA -w-
00F : Selector = 0078, Base = 80400000, Limit = 0000FFFF, DPL0, Type = CODE -r-
010 : Selector = 0080, Base = 80400000, Limit = 0000FFFF, DPL0, Type = DATA -w-
011 : Selector = 0088, Base = 00000000, Limit = 00000000, DPL0, Type = DATA -w-
014 : Selector = 00A0, Base = 814985A8, Limit = 00000068, DPL0, Type = TSS32 a
01C : Selector = 00E0, Base = F0430000, Limit = 0000FFFF, DPL0, Type = CODE cra
01D : Selector = 00E8, Base = 00000000, Limit = 0000FFFF, DPL0, Type = DATA -w-
01E : Selector = 00F0, Base = 8042DCE8, Limit = 000003B7, DPL0, Type = CODE —-
01F : Selector = 00F8, Base = 00000000, Limit = 0000FFFF, DPL0, Type = DATA -w-
020 : Selector = 0100, Base = F0440000, Limit = 0000FFFF, DPL0, Type = DATA -wa
021 : Selector = 0108, Base = F0440000, Limit = 0000FFFF, DPL0, Type = DATA -wa
022 : Selector = 0110, Base = F0440000, Limit = 0000FFFF, DPL0, Type = DATA –wa
[...]

Table 4-6. GDT Selectors Defined in ntddk.h

SYMBOL

VALUE

COMMENTS

KGDT_NULL

0x0000

Null segment selector (invalid)

KGDT_R0_CODE

0x0008

CS register in kernel-mode

KGDT_R0_DATA

0x0010

SS register in kernel-mode

KGDT_R3_CODE

0x0018

CS register in user-mode

KGDT_R3_DATA

0x0020

DS, ES, and SS register in user-mode, DS and ES register in kernel-mode

KGDT_TSS

0x0028

Task State Segment in user- and kernel-mode

KGDT_R0_PCR

0x0030

FS register in kernel-mode (Processor Control Region)

KGDT_R3_TEB

0x0038

FS register in user-mode (Thread Environment Block)

KGDT_VDM_TILE

0x0040

Base 0x00000400, limit 0x0000FFFF (Virtual DOS Machine)

KGDT_LDT

0x0048

Local Descriptor Table

KGDT_DF_TSS

0x0050

ntoskrnl.exe variable KiDoubleFaultTSS

KGDT_NMI_TSS

0x0058

ntoskrnl.exe variable KiNMITSS


The selectors in Example 4-14 that are not listed in Table 4-6 can in part be identified by looking for familiar base addresses or memory contents, and by using the Kernel Debugger to look up the symbols for some of the base addresses. Table 4-7 comprises the selectors that I have identified so far.

The +i option of w2k_mem.exe dumps the gate descriptors stored in the IDT. Example 4-15 is an excerpt from this rather long list, comprising only the first 20 entries that have a predefined meaning assigned by Intel (Intel 1999c, pp. 5-6). Interrupts 0x14 to 0x1F are reserved for Intel; the remaining range 0x20 to 0xFF is available to the operating system.

In Table 4-8, I have summarized all interrupts that refer to identifiable and nontrivial interrupt, trap, and task gates. Most of the user defined interrupts point to dummy handlers named KiUnexpectedInterruptNNN(), as explained earlier in this chapter. Some interrupt handlers are located at addresses that can't be resolved to symbols by the Kernel Debugger.

Table 4-7. More GDT Selectors

VALUE

BASE

DESCRIPTION

0x0078

0x80400000

ntoskrnl.exe code segment

0x0080

0x80400000

ntoskrnl.exe data segment

0x00A0

0x814985A8

TSS (EIP member points to HalpMcaExceptionHandlerWrapper)

0x00E0

0xF0430000

ROM BIOS code segment

0x00F0

0x8042DCE8

ntoskrnl.exe function KiI386CallAbios

0x0100

0xF0440000

ROM BIOS data segment

0x0108

0xF0440000

ROM BIOS data segment

0x0110

0xF0440000

ROM BIOS data segment


Example 4-15. Displaying IDT Gate Descriptors

E:\>w2k_mem +i
[...]
IDT information:
----------------

00 : Pointer = 0008:804625E6, Base = 00000000, Limit = FFFFFFFF, Type = INT32
01 : Pointer = 0008:80462736, Base = 00000000, Limit = FFFFFFFF, Type = INT32
02 : TSS     = 0058,          Base = 804700A8, Limit = 00000068, Type = TASK
03 : Pointer = 0008:80462A0E, Base = 00000000, Limit = FFFFFFFF, Type = INT32
04 : Pointer = 0008:80462B72, Base = 00000000, Limit = FFFFFFFF, Type = INT32
05 : Pointer = 0008:80462CB6, Base = 00000000, Limit = FFFFFFFF, Type = INT32
06 : Pointer = 0008:80462E1A, Base = 00000000, Limit = FFFFFFFF, Type = INT32
07 : Pointer = 0008:80463350, Base = 00000000, Limit = FFFFFFFF, Type = INT32
08 : TSS     = 0050,          Base = 80470040, Limit = 00000068, Type = TASK
09 : Pointer = 0008:8046370C, Base = 00000000, Limit = FFFFFFFF, Type = INT32
0A : Pointer = 0008:80463814, Base = 00000000, Limit = FFFFFFFF, Type = INT32
0B : Pointer = 0008:80463940, Base = 00000000, Limit = FFFFFFFF, Type = INT32
0C : Pointer = 0008:80463C44, Base = 00000000, Limit = FFFFFFFF, Type = INT32
0D : Pointer = 0008:80463E50, Base = 00000000, Limit = FFFFFFFF, Type = INT32
0E : Pointer = 0008:804648A4, Base = 00000000, Limit = FFFFFFFF, Type = INT32
0F : Pointer = 0008:80464C3F, Base = 00000000, Limit = FFFFFFFF, Type = INT32
10 : Pointer = 0008:80464D47, Base = 00000000, Limit = FFFFFFFF, Type = INT32
11 : Pointer = 0008:80464E6B, Base = 00000000, Limit = FFFFFFFF, Type = INT32
12 : TSS     = 00A0,          Base = 814985A8, Limit = 00000068, Type = TASK
13 : Pointer = 0008:80464C3F, Base = 00000000, Limit = FFFFFFFF, Type = INT32
[...]

Table 4-8. Windows 2000 Interrupt, Trap, and Task Gates

INT

INTEL DESCRIPTION

OWNER

HANDLER/TSS

0x00

Divide Error (DE)

ntoskrnl.exe

KiTrap00

0x01

Debug (DB)

ntoskrnl.exe

KiTrap01

0x02

NMI Interrupt

ntoskrnl.exe

KiNMITSS

0x03

Breakpoint (BP)

ntoskrnl.exe

KiTrap03

0x04

Overflow (OF)

ntoskrnl.exe

KiTrap04

0x05

BOUND Range Exceeded (BR)

ntoskrnl.exe

KiTrap05

0x06

Undefined Opcode (UD)

ntoskrnl.exe

KiTrap06

0x07

No Math Coprocessor (NM)

ntoskrnl.exe

KiTrap07

0x08

Double Fault (DF)

ntoskrnl.exe

KiDouble

0x09

Coprocessor Segment Overrun

ntoskrnl.exe

KiTrap09

0x0A

Invalid TSS (TS)

ntoskrnl.exe

KiTrap0A

0x0B

Segment Not Present (NP)

ntoskrnl.exe

KiTrap0B

0x0C

Stack-Segment Fault (SS)

ntoskrnl.exe

KiTrap0C

0x0D

General Protection (GP)

ntoskrnl.exe

KiTrap0D

0x0E

Page Fault (PF)

ntoskrnl.exe

KiTrap0E

0x0F

(Intel reserved)

ntoskrnl.exe

KiTrap0F

0x10

Math Fault (MF)

ntoskrnl.exe

KiTrap10

0x11

Alignment Check (AC)

ntoskrnl.exe

KiTrap11

0x12

Machine Check (MC)

?

?

0x13

Streaming SIMD Extensions

ntoskrnl.exe

KiTrap0F

0x14-0x1F

(Intel reserved)

ntoskrnl.exe

KiTrap0F

0x2A

User Defined

ntoskrnl.exe

KiGetTickCount

0x2B

User Defined

ntoskrnl.exe

KiCallbackReturn

0x2C

User Defined

ntoskrnl.exe

KiSetLowWaitHighThread

0x2D

User Defined

ntoskrnl.exe

KiDebugService

0x2E

User Defined

ntoskrnl.exe

KiSystemService

0x2F

User Defined

ntoskrnl.exe

KiTrap0F

0x30

User Defined

hal.dll

HalpClockInterrupt

0x38

User Defined

hal.dll

HalpProfileInterrupt


Windows 2000 Memory Areas

The last w2k_mem.exe option that remains to be discussed is the +b switch. It generates an enormously long list of contiguous memory regions within the 4-GB linear address space. w2k_mem.exe builds this list by walking through the entire PTE array at address 0xC0000000, using the spy device's IOCTL function SPY_IO_PAGE_ENTRY. The dSize member contained in each resulting SPY_PAGE_ENTRY structure is added to the linear address associated with the PTE to get the linear address of the next PTE to be retrieved. Listing 4-30 shows the implementation of this option.

Listing 4-30. Finding Contiguous Linear Memory Blocks

DWORD WINAPI DisplayMemoryBlocks (HANDLE hDevice)
    {
    SPY_PAGE_ENTRY spe;
    PBYTE          pbPage, pbBase;
    DWORD          dBlock, dPresent, dTotal;
    DWORD          n = 0;

    pbPage   = 0;
    pbBase   = INVALID_ADDRESS;
    dBlock   = 0;
    dPresent = 0;
    dTotal   = 0;

    n += _printf (L"\r\nContiguous memory blocks:"
                  L"\r\n-------------\r\n\r\n");

    do  {
        if (!IoControl (hDevice, SPY_IO_PAGE_ENTRY,
                        &pbPage, PVOID_,
                        &spe,    SPY_PAGE_ENTRY_))
            {
            n += _printf (L" !!! Device I/O error !!!\r\n");
            break;
            }
        if (spe.fPresent)
            {
            dPresent += spe.dSize;
            }
        if (spe.pe.dValue)
            {
            dTotal += spe.dSize;

            if (pbBase == INVALID_ADDRESS)
                {
                n += _printf (L"%5lu : 0x%08lX ->",
                              ++dBlock, pbPage);

                pbBase = pbPage;
                }
            }
        else
            {
            if (pbBase != INVALID_ADDRESS)
                {
                n += _printf (L" 0x%08lX (0x%08lX bytes)\r\n",
                              pbPage-1, pbPage-pbBase);                pbBase = INVALID_ADDRESS;
                }
            }
        }
    while (pbPage += spe.Size);

    if (pbBase != INVALID_ADDRESS)
        {
        n += _printf (L"0x%08lX\r\n", pbPage-1);
        }
    n += _printf (L"\r\n"
                  L" Present bytes: 0x%08lX\r\n"
                  L" Total   bytes: 0x%08lX\r\n",
                  dPresent, dTotal);
    return n;
    }

Example 4-16 is an excerpt from a sample run on my machine, showing some of the more interesting regions. Some very obvious addresses are 0x00400000, where the image of w2k_mem.exe starts (block #13), and 0x10000000, where the image of w2k_lib.dll is located (block #23). The TEB and PEB pages also are clearly discernible (block #104), as are the hal.dll, ntoskrnl.exe, and win32k.sys areas (blocks #105 and 106). Blocks #340 to 350 are, of course, the valid fragments of the system's PTE array, featuring the page-directory as part of block #347. Block #2122 contains the SharedUserData area, and #2123 comprises the KPCR, KPRCB, and CONTEXT structures containing thread and processor status information.

Example 4-16. A Sample List of Contiguous Memory Blocks

E:\>w2k_mem +b
[...]
Contiguous memory blocks:
-------------------------

    1 : 0x00010000 -> 0x00010FFF (0x00001000 bytes)
    2 : 0x00020000 -> 0x00020FFF (0x00001000 bytes)
    3 : 0x0012D000 -> 0x00138FFF (0x0000C000 bytes)
    4 : 0x00230000 -> 0x00230FFF (0x00001000 bytes)
    5 : 0x00240000 -> 0x00241FFF (0x00002000 bytes)
    6 : 0x00247000 -> 0x00247FFF (0x00001000 bytes)
    7 : 0x0024F000 -> 0x00250FFF (0x00002000 bytes)
    8 : 0x00260000 -> 0x00260FFF (0x00001000 bytes)
    9 : 0x00290000 -> 0x00290FFF (0x00001000 bytes)
   10 : 0x002E0000 -> 0x002E0FFF (0x00001000 bytes)
   11 : 0x002E2000 -> 0x002E3FFF (0x00002000 bytes)
   12 : 0x003B0000 -> 0x003B1FFF (0x00002000 bytes)
   13 : 0x00400000 -> 0x00404FFF (0x00005000 bytes)
   14 : 0x00406000 -> 0x00406FFF (0x00001000 bytes)
   15 : 0x00410000 -> 0x00410FFF (0x00001000 bytes)
   16 : 0x00419000 -> 0x00419FFF (0x00001000 bytes)
   17 : 0x0041B000 -> 0x0041BFFF (0x00001000 bytes)
   18 : 0x00450000 -> 0x00450FFF (0x00001000 bytes)
   19 : 0x00760000 -> 0x00760FFF (0x00001000 bytes)
   20 : 0x00770000 -> 0x00770FFF (0x00001000 bytes)
   21 : 0x00780000 -> 0x00783FFF (0x00004000 bytes)
   22 : 0x00790000 -> 0x00791FFF (0x00002000 bytes)
   23 : 0x10000000 -> 0x10003FFF (0x00004000 bytes)
   24 : 0x10005000 -> 0x10005FFF (0x00001000 bytes)
   25 : 0x1000E000 -> 0x10016FFF (0x00009000 bytes)
   26 : 0x759B0000 -> 0x759B1FFF (0x00002000 bytes)
[...]
  103 : 0x7FFD2000 -> 0x7FFD3FFF (0x00002000 bytes)
  104 : 0x7FFDE000 -> 0x7FFE0FFF (0x00003000 bytes)
  105 : 0x80000000 -> 0xA01A5FFF (0x201A6000 bytes)
  106 : 0xA01B0000 -> 0xA01F2FFF (0x00043000 bytes)
  107 : 0xA0200000 -> 0xA02C7FFF (0x000C8000 bytes)
  108 : 0xA02F0000 -> 0xA03FFFFF (0x00110000 bytes)
  109 : 0xA4000000 -> 0xA4001FFF (0x00002000 bytes)
  110 : 0xBE63B000 -> 0xBE63CFFF (0x00002000 bytes)
[...]
  340 : 0xC0000000 -> 0xC0001FFF (0x00002000 bytes)
  341 : 0xC0040000 -> 0xC0040FFF (0x00001000 bytes)
  342 : 0xC01D6000 -> 0xC01D6FFF (0x00001000 bytes)
  343 : 0xC01DA000 -> 0xC01DAFFF (0x00001000 bytes)
  344 : 0xC01DD000 -> 0xC01E0FFF (0x00004000 bytes)
  345 : 0xC01FD000 -> 0xC01FDFFF (0x00001000 bytes)
  346 : 0xC01FF000 -> 0xC0280FFF (0x00082000 bytes)
  347 : 0xC0290000 -> 0xC0301FFF (0x00072000 bytes)
  348 : 0xC0303000 -> 0xC0386FFF (0x00084000 bytes)
  349 : 0xC0389000 -> 0xC038CFFF (0x00004000 bytes)
  350 : 0xC039E000 -> 0xC03FFFFF (0x00062000 bytes)
[...]
 2121 : 0xFFC00000 -> 0xFFD0FFFF (0x00110000 bytes)
 2122 : 0xFFDF0000 -> 0xFFDF0FFF (0x00001000 bytes)
 2123 : 0xFFDFF000 -> 0xFFDFFFFF (0x00001000 bytes)
[...]
 Present bytes: 0x22AA9000
 Total   bytes: 0x2B8BA000
[...]

The odd thing about the +b option of w2k_mem.exe is that it reports an amount of used memory that is far beyond any reasonable value. Note the summary lines at the end of Example 4-16. Am I really using 700 MB of memory now? The Windows 2000 Task Manager indicates 150 MB—so what's going on here? This strange effect comes from memory block #105, which is reported to range from 0x80000000 to 0xA01A5FFF, spanning 0x201A6000 bytes, which equals 538,599,424 bytes. This is obviously nonsense. The problem is that the entire linear address range from 0x80000000 to 0x9FFFFFFF is mapped to the physical address range 0x00000000 to 0x1FFFFFFF, as already noted earlier in this chapter. All 4-MB pages in this range have valid PDEs in the page-directory at address 0xC0300000, which can be proved by issuing the command w2k_mem +d #0x200 0xC0300800 (Example 4-17). Because all PDEs in the resulting list are odd numbers, the corresponding pages must be present; however, they are not necessarily backed up by physical memory. In fact, large portions of this memory range are really "holes" and seem to be filled with 0xFF bytes if copied to a buffer. Therefore, you shouldn't take the memory usage summary displayed by w2k_mem.exe too seriously.

Example 4-17. The PDEs of the Address Range 0x80000000 to 0x9FFFFFFF

E:\>w2k_mem +d #0x200 0xC0300800
[...]
C0300800..C03009FF: 512 valid bytes

Address  | 00000000 - 00000004 : 00000008 - 0000000C | 0000 0004 0008 000C
---------|---------------------:---------------------|--------------------
C0300800 | 000001E3 - 004001E3 : 008001E3 - 00C001E3 | ...ã .@.ã .?.ã .À.ã
C0300810 | 010001E3 - 014001E3 : 018001E3 - 01C001E3 | ...ã .@.ã .?.ã .À.ã
C0300820 | 020001E3 - 024001E3 : 028001E3 - 02C001E3 | ...ã .@.ã .?.ã .À.ã
C0300830 | 030001E3 - 034001E3 : 038001E3 - 03C001E3 | ...ã .@.ã .?.ã .À.ã
C0300840 | 040001E3 - 044001E3 : 048001E3 - 04C001E3 | ...ã .@.ã .?.ã .À.ã
C0300850 | 050001E3 - 054001E3 : 058001E3 - 05C001E3 | ...ã .@.ã .?.ã .À.ã
C0300860 | 060001E3 - 064001E3 : 068001E3 - 06C001E3 | ...ã .@.ã .?.ã .À.ã
C0300870 | 070001E3 - 074001E3 : 078001E3 - 07C001E3 | ...ã .@.ã .?.ã .À.ã
C0300880 | 080001E3 - 084001E3 : 088001E3 - 08C001E3 | ...ã .@.ã .?.ã .À.ã
C0300890 | 090001E3 - 094001E3 : 098001E3 - 09C001E3 | ...ã .@.ã .?.ã .À.ã
C03008A0 | 0A0001E3 - 0A4001E3 : 0A8001E3 - 0AC001E3 | ...ã .@.ã .?.ã .À.ã
C03008B0 | 0B0001E3 - 0B4001E3 : 0B8001E3 - 0BC001E3 | ...ã .@.ã .?.ã .À.ã
C03008C0 | 0C0001E3 - 0C4001E3 : 0C8001E3 - 0CC001E3 | ...ã .@.ã .?.ã .À.ã
C03008D0 | 0D0001E3 - 0D4001E3 : 0D8001E3 - 0DC001E3 | ...ã .@.ã .?.ã .À.ã
C03008E0 | 0E0001E3 - 0E4001E3 : 0E8001E3 - 0EC001E3 | ...ã .@.ã .?.ã .À.ã
C03008F0 | 0F0001E3 - 0F4001E3 : 0F8001E3 - 0FC001E3 | ...ã .@.ã .?.ã .À.ã
C0300900 | 100001E3 - 104001E3 : 108001E3 - 10C001E3 | ...ã .@.ã .?.ã .À.ã
C0300910 | 110001E3 - 114001E3 : 118001E3 - 11C001E3 | ...ã .@.ã .?.ã .À.ã
C0300920 | 120001E3 - 124001E3 : 128001E3 - 12C001E3 | ...ã .@.ã .?.ã .À.ã
C0300930 | 130001E3 - 134001E3 : 138001E3 - 13C001E3 | ...ã .@.ã .?.ã .À.ã
C0300940 | 140001E3 - 144001E3 : 148001E3 - 14C001E3 | ...ã .@.ã .?.ã .À.ã
C0300950 | 150001E3 - 154001E3 : 158001E3 - 15C001E3 | ...ã .@.ã .?.ã .À.ã
C0300960 | 160001E3 - 164001E3 : 168001E3 - 16C001E3 | ...ã .@.ã .?.ã .À.ã
C0300970 | 170001E3 - 174001E3 : 178001E3 - 17C001E3 | ...ã .@.ã .?.ã .À.ã
C0300980 | 180001E3 - 184001E3 : 188001E3 - 18C001E3 | ...ã .@.ã .?.ã .À.ã
C0300990 | 190001E3 - 194001E3 : 198001E3 - 19C001E3 | ...ã .@.ã .?.ã .À.ã
C03009A0 | 1A0001E3 - 1A4001E3 : 1A8001E3 - 1AC001E3 | ...ã .@.ã .?.ã .À.ã
C03009B0 | 1B0001E3 - 1B4001E3 : 1B8001E3 - 1BC001E3 | ...ã .@.ã .?.ã .À.ã
C03009C0 | 1C0001E3 - 1C4001E3 : 1C8001E3 - 1CC001E3 | ...ã .@.ã .?.ã .À.ã
C03009D0 | 1D0001E3 - 1D4001E3 : 1D8001E3 - 1DC001E3 | ...ã .@.ã .?.ã .À.ã
C03009E0 | 1E0001E3 - 1E4001E3 : 1E8001E3 - 1EC001E3 | ...ã .@.ã .?.ã .À.ã
C03009F0 | 1F0001E3 - 1F4001E3 : 1F8001E3 - 1FC001E3 | ...ã .@.ã .?.ã .À.ã
[...]

The Windows 2000 Memory Map

The last part of this chapter is dedicated to the general layout of the 4-GB linear address space as it is "seen" by a Windows 2000 process. Table 4-9 lists the address ranges of various essential data structures. The big holes between them are used for several purposes, such as load areas for process modules and device drivers, memory pools, working set lists, and the like. Note that some addresses and block sizes might vary considerably from system to system, depending on the memory and hardware configuration, the process properties, and several other variables. Therefore, use this list only as a rough sketch, not as an accurate roadmap.

Some physical memory blocks appear twice or more in the linear address space. For example, the SharedUserData area at linear address 0xFFDF0000 is mirrored at address 0x7FFE0000. Both refer to the same page in physical memory—writing a byte to 0xFFDF0000+n mysteriously changes the value of the byte at 0x7FFE0000+n. This is the world of virtual memory—a physical address can be mapped anywhere into the linear address space, even to several addresses at the same time. It's just a matter of setting up the page-directory and page-tables appropriately. Please recall Figures 4-3 and 4-4, which clearly show that linear addresses are fake. Their Directory and Table bit fields are just pointers to structures that determine the real location of the data. And if the PFNs of two PTEs happen to be identical, the corresponding linear addresses refer to the same physical memory location.

Table 4-9. Identifiable Memory Regions in the Address Space of a Process

START

END

HEX SIZE

TYPE/DESCRIPTION

0x00000000

0x0000FFFF

10000

Lower guard block

0x00010000

0x0001FFFF

10000

WCHAR[]/Environment strings, allocated in 4-KB pages

0x00020000

0x0002FFFF

10000

PROCESS_PARAMETERS/allocated in 4-KB pages

0x00030000

0x0012FFFF

100000

DWORD [4000]/Process stack (default: 1 MB)

0x7FFDD000

0x7FFDDFFF

1000

TEB/Thread Environment Block of thread #2

0x7FFDE000

0x7FFDEFFF

1000

TEB/Thread Environment Block of thread #1

0x7FFDF000

0x7FFDFFFF

1000

PEB/Process Environment Block

0x7FFE0000

0x7FFE02D7

2D8

KUSER_SHARED_DATA/SharedUserData in user-mode

0x7FFF0000

0x7FFFFFFF

10000

Upper guard block

0x80000000

0x800003FF

400

IVT/Interrupt Vector Table

0x80036000

0x800363FF

400

KGDTENTRY[80]/Global Descriptor Table

0x80036400

0x80036BFF

800

KIDTENTRY[100]/Interrupt Descriptor Table

0x800C0000

0x800FFFFF

40000

VGA/ROM BIOS

0x80244000

0x802460AA

20AB

KTSS/user/kernel Task State Segment (busy)

0x8046AB80

0x8046ABBF

40

KeServiceDescriptorTable

0x8046ABC0

0x8046ABFF

40

KeServiceDescriptorTableShadow

0x80470040

0x804700A7

68

KTSS/KiDoubleFaultTSS

0x804700A8

0x8047010F

68

KTSS/KiNMITSS

0x804704D8

0x804708B7

3E0

PROC[F8]/KiServiceTable

0x804708B8

0x804708BB

4

DWORD/KiServiceLimit

0x804708BC

0x804709B3

F8

BYTE[F8]/KiArgumentTable

0x814C6000

0x82CC5FFF

1800000

PFN[100000]/MmPfnDatabase (max. for 4 GB)

0xA01859F0

0xA01863EB

9FC

PROC[27F]/W32pServiceTable

0xA0186670

0xA01868EE

27F

BYTE[27F]/W32pArgumentTable

0xC0000000

0xC03FFFFF

400000

X86_PE[100000]/page-directory and page-tables

0xC1000000

0xE0FFFFFF

20000000

System Cache (MmSystemCacheStart, MmSystemCacheEnd)

0xE1000000

0xE77FFFFF

6800000

Paged Pool (MmPagedPoolStart, MmPagedPoolEnd)

0xF0430000

0xF043FFFF

10000

ROM BIOS code segment

0xF0440000

0xF044FFFF

10000

ROM BIOS data segment

0xFFDF0000

0xFFDF02D7

2D8

KUSER_SHARED_DATA/SharedUserData in kernel-mode

0xFFDFF000

0xFFDFF053

54

KPCR/Processor Control Region (kernel-mode FS segment)

0xFFDFF120

0xFFDFF13B

1C

KPRCB/Processor Control Block

0xFFDFF13C

0xFFDFF407

2CC

CONTEXT/Thread Context (CPU state)

0xFFDFF620

0xFFDFF71F

100

Lookaside list directories


  • + Share This
  • 🔖 Save To Your Account

Related Resources

There are currently no related titles. Please check back later.