Deskriptory virtuálních adres 4: Reálně se vyskytující deskriptory a závěr

V posledním dílu malého seriálu o deskriptorech virtuálních adres si ukážeme, jakými deskriptory operační systém popisuje paměť alokovanou či rezervovanou pro různé účely, jako je mapování souboru, mapování fyzické paměti pomocí rozšíření AWE či alokace privátních stránek provedená klasickým způsobem.

Další části tohoto článku se již deskriptorům virtuálních adres nevěnují. Popisují nástroje, které jsem při výzkumu pro tento seriál naprogramoval a používal a jaké testy jsem s nimi prováděl. Všechny nástroje jsou volně dostupné včetně zdrojových kódů. Pokud tedy budete mít trpělivost, můžete zkusit moje výsledky ověřit.

Závěrečná část článku obsahuje definice struktur deskriptorů virtuálních adres pro starší verze Windows. Jedná se pouze o definice, text nijak nevysvětluje případné odlišnosti v používání těchto struktur operačních systémem. Nevylučuji však, že tomuto tématu věnuji nějaký další článek.

Příklady reálně pozorovaných deskriptorů

Windows dovoluje využívat virtuální adresový prostor procesu různými způsoby, což se odráží i na množství různých typů deskriptorů virtuálních adres, na které lze při pohledu pod povrch adresového prostoru narazit. V této části textu se podíváme na to, jaké deskriptory vznikají při alokaci či rezervování paměti pro různé účely.

Upozornění: Pokusy jsem prováděl pouze s bloky paměti, jejichž velikost ve většině případů nepřesahovala 64 KB. Pokud se tématem deskriptorů virtuálních adres plánujete zabývat, můžete při vlastních experimentech narazit na odlišné chování systému právě z důvodu práce s velkými bloky paměti. Druhým (a mnohem prozaičtějším) důvodem odlišnosti našich výsledků může být chyba v interpretaci výsledků mnou prováděných testů.

Privátní paměť

K práci s privátní pamětí slouží funkce Windows API VirtualAlloc a VirtualAllocEx. Dovolují oblast virtuálního adresového prostoru procesu rezervovat, alokovat pro běžné účely, vytvářet stránky s monitorovanými zápisy (write watch) či mapovat přímo rámce fyzické paměti.

Poznámka: Rozhraní Windows API dovoluje provádět s virtuální pamětí i další operace, například změnit oprávnění jednotlivých stránek (VirtualProtect, VirtualProtectEx) či číst nebo měnit obsah adresového prostoru jiného procesu (ReadProcessMemory, WriteProcessMemory). Vzhledem ke svému vlivu na deskriptory virtuálních adres se však takovými operacemi v této části textu zabývat nebudeme. Důležitá je pouze alokace a rezervace, při níž dochází ke vzniku deskriptorů a případně dealokace (VirtualFree, VirtualFreeEx), která má za příčinu jejich zánik.

Jádro operačního systému pro reprezentaci bloků alokované či rezervované privátní paměti používá krátké deskriptory, které se změní na dlouhé, pokud je na jimi reprezentovanou oblast (nebo její část či části) použita funkce MmSecureVirtualMemory. Příznak PrivateMemory mají nastaven na 1, čímž se liší od deskriptorů popisujících paměťově mapované soubory.

Rezervováním oblasti paměti vzniká deskriptor typu VadNone, který má všechny příznaky první a třetí skupiny nastaveny na 0, vyjma položky oprávnění (Protection) a výše zmiňovaného PrivateMemory. Protože při rezervaci nedochází k žádnému přidělování fyzické paměti, na povolenou paměťovou kvótu procesu tato operace nemá vliv. Paměť lze rezervovat prostřednictvím rutin VirtualAlloc a VirtualAllocEx specifikováním příznaku MEM_RESERVE.

Od rezervované paměti se jen málo liší paměť s monitorováním zápisů. Deskriptor takové oblasti se od předchozího případu odlišuje pouze položkou VadType v první skupině příznaků, která nabývá hodnoty VadWriteWatch. Paměť lze rezervovat s monitorováním zápisů specifikováním příznaku MEM_WRITE_WATCH při volání některé z výše zmíněných alokačních funkcí. Jelikož se v podstatě jedná o paměť rezervovanou, neovlivňuje paměťovou kvótu procesu.

Paměť je dále možné rezervovat pro účely rozhraní AWE (Address Windowing Extension). Takovou oblast popisuje deskriptor typu VadAwe. Až na typ se všechen jeho obsah shoduje s deskriptorem VadNone popisujícím obyčejnou rezervovanou paměť. K rezervování paměti pro účely rozhraní AWE je nutné při volání alokační funkce uvést příkaz MEM_PHYSICAL. Daná oblast se stává rezervovanou, dokud nedojde k volání rutiny MapUserPhysicalPages, která jednotlivé virtuální stránky namapuje na příslušné rámce. Nastavení mapování nemá vliv na obsah příslušného deskriptoru. Nemění se ani položka CommitCharge, jenž, jak se dozvíte níže, indikuje, kolik malých stránek daná oblast odebírá z paměťové kvóty procesu. Důvodem je pravděpodobně fakt, že k tvorbě deskriptoru dochází při rezervování virtuálních stránek, což „žádnou“ fyzickou paměť nestojí, nikoliv při alokaci fyzických rámců. A deskriptory popisují svými příznaky stav virtuálního adresového prostoru, ne toho, na jaké rámce se jednotlivé virtuální adresy překládají.

Poznámka: I při rezervaci dochází k jisté spotřebě paměti, například na uložení deskriptoru, jenž nově rezervovanou oblast popisuje. Deskriptory jsou však ukládány do stránkovaného fondu paměti jádra, kterou proces nemůže přímo využívat. Z tohoto důvodu pravděpodobně není rezervovaná paměť započítávána do povolené paměťové kvóty procesu.

Alokováním virtuální paměti pro běžné účely (příznak MEM_COMMIT při volání výše zmíněných alokačních funkcí) dojde k vytvoření deskriptoru typu VadNone, který se od toho popisujícího obyčejnou rezervovanou paměť liší pouze ve dvou položkách: příznak MemCommit je nastaven na 1 a položka CommitCharge udává, kolik malých stránek alokovaný blok odebírá z paměťové kvóty procesu.

Příznak MemCommit je však nastaven na 1 pouze v případě přímé alokace (změny stavu stránek dané oblasti z volná (free) na alokovaná (committed)). Pokud při alokaci dochází pouze k přechodu ze stavu rezervovaná (reserved), hodnota příznaku zůstává nulová a změnu odráží pouze nastavení položky CommitCharge, která udává počet alokovaných malých stránek v oblasti popisované daným deskriptorem. Z obsahu deskriptoru se tedy nedozvíte, jaké podoblasti bloku jsou alokována či rezervovány.

Privátní paměť lze také alokovat z velkých stránek, což má za následek vznik deskriptoru typu VadLargePages. Příznak MemCommit je při přímé alokaci nastaven na 1 a CommitCharge udává velikost alokované oblasti ve stránkách malé velikosti. K alokaci velkých stránek je nutné v dané alokační funkci uvést příznak MEM_LARGE_PAGES.

Poznámka: Příznak MemCommit není nastaven na 1 v případě, že jsou paměťové stránky nejprve rezervovány a alokovány až druhým voláním alokační funkce.

Paměťově mapované soubory

Oblasti adresového prostoru využívané paměťově mapovanými soubory reprezentují normální deskriptory, které se v případě použití rutiny MmSecureVirtualMemory mění na deskriptory dlouhé. Příznak PrivateMemory je nastaven na 0, stejně jako MemCommit a CommitCharge. Správce paměti používá k uložení potřebných informací hlavně druhou skupinu příznaků a další položky, které se ve struktuře krátkého deskriptoru nevyskytují.

Poměrně zajímavým způsobem se chová pole Protection v první skupině příznaků. Paměťově mapované soubory lze používat i s optimalizací copy on write, jejíž zapnutí spočívá v nastavení specifického oprávnění stránkám paměťového bloku (PAGE_WRITECOPY, PAGE_EXECURE_WRITECOPY). Moje pokusy ukazují, že hodnota pole Protection zapnutí této optimalizace nerespektuje, ačkoliv pro taková oprávnění existují konstanty (viz předchozí díly seriálu). Místo toho se toto pole chová, jako by žádný režim copy on write nastaven nebyl. Výjimku z pravidla představují mapované soubory formátu PE, jejichž deskriptory mají v poli Protection hodnotu 7, která odpovídá oprávnění PAGE_EXECUTE_WRITECOPY.

Zda daný blok virtuálních stránek podléhá optimalizaci copy on write, se lze s větší spolehlivostí dozvědět z příznaku CopyOnWrite, který je v kladném případě nastaven na 1. Správce paměti jej však nastavuje i v případě, že ji v požadovaném oprávnění namapovaného bloku paměti neuvedete.

Krom mapování diskových souborů či vytváření sdílené paměti, kteréžto případy reprezentují vždy deskriptory typu VadNone, existují ještě tři speciální případy, pro které správce paměti také používá normální deskriptory.

Prvním z nich je mapování souboru formátu PE. Při vytváření objektu Section, kterým jádro všechny druhy sdílené paměti a paměťově mapovaných souborů reprezentuje, je nutné uvést příznak SEC_IMAGE. V opačném případě systém namapuje i soubor formátu PE jako klasický datový soubor reprezentovaný deskriptorem typu VadNone. Každý namapovaný soubor formátu PE reprezentuje jeden deskriptor typu VadImageMap.

Druhý speciální případ tvoří sdílená paměť tvořená velkými stránkami. Správce paměti ji reprezentuje deskriptorem typu VadLargePageSection. Třetí speciální případ odpovídá mapování objektu DevicePhysicalMemory, jenž reprezentuje fyzickou paměť počítače. Pro tento případ systém používá deskriptory typu VadDevicePhysicalMemory.

Použité nástroje

Během psaní tohoto textu jsem také strávil určitou dobu programováním několika jednoduchých aplikací. Podle účelu lze tyto utility rozdělit na dvě skupiny. První skupina nástrojů se zaměřuje na odhalování struktury virtuálního adresového prostoru cílového procesu, a to jak pomocí funkce VirtualQueryEx, tak zobrazováním obsahů jednotlivých deskriptorů virtuálních adres. Patří do ní aplikace Vad, jejíž první verze se objevila spolu s vydáním mé knihy, a utilita Vq. Programy a knihovny náležející do druhé skupiny si kladou za cíl alokovat či rezervovat virtuální paměť pro různé účely a tím vytvořit co největší množství různých deskriptorů. Tímto způsobem lze také zjistit, jak správce paměti různým způsobem alokované či rezervované oblasti adresového prostoru reprezentuje a jaké informace si o nich uchovává.

Vq

Tuto utilitu již dobře znáte z prvního dílu seriálu, především z prvních tří výpisů. Program jako vstup akceptuje číslo PID cílového procesu v podobě argumentu příkazové řádky. Pokud jej spustíte bez parametrů, jako cílový proces nastaví sám sebe.

Výstupem programu je popis struktury adresového prostoru cílového procesu. Struktura a význam těchto informací byly popsány v úvodní části „Povrch adresového prostoru.“

VadTest

Tento program rezervuje a alokuje virtuální paměť různými způsoby a nastavuje virtuálním stránkám různá oprávnění. Za cíl si klade vytvořit co největší počet deskriptorů různého druhu a s různými hodnotami příznaků v jejich struktuře.

Program provádí sérii tzv. testů. Každý test spočívá v alokaci či rezervování jedná či několika oblastí adresového prostoru pro určité účely. Pokud se některý z kroků nezdaří, program v daném testu dále pokračuje, pouze na standardní výstup vypíše informace o chybě. Každý test vypisuje na standardní výstup adresy oblastí alokované či rezervované paměti. Po dokončení alokace (rezervace) program čeká na stisk klávesy od uživatele. Dává vám tak možnost prohlédnout si adresový prostor procesu vadtest.exe a podívat se, jakými deskriptory jádro jednotlivé oblasti adresového prostoru týkající se testu popisuje. Všechny testy jsou prováděny v adresovém prostoru vlastního programu (vadtest.exe).

Program VadTest provádí následující testy:

  • přímá alokace bloku privátní paměti s různým oprávněním (čtení, zápis, spouštění kódu, žádný přístup a jejich kombinace),

  • přímá alokace bloku privátní paměti s různým oprávněním (čtení, zápis, spouštění kódu a jejich kombinace) s příznakem PAGE_NOCACHE,

  • přímá alokace bloku privátní paměti s různým oprávněním (čtení, zápis, spouštění kódu a jejich kombinace) s příznakem PAGE_GUARD,

  • přímá alokace bloku privátní paměti s různým oprávněním (čtení, zápis, spouštění kódu a jejich kombinace) s příznakem PAGE_WRITECOMBINE,

  • přímá alokace bloku privátní paměti tvořeného velkými stránkami (předchozí testy vždy alokovaly pouze malé stránky),

  • rezervování oblasti privátní paměti,

  • rezervování oblasti privátní paměti se zapnutým monitorováním zápisů,

  • nepřímá alokace bloku privátní paměti s různým oprávněním (čtení, zápis, spouštění kódu, žádný přístup a jejich kombinace). Kód testu nejprve daný region paměti rezervuje a až dalším voláním VirtualAlloc alokuje;

  • přímá alokace bloku privátní paměti následovaná změnou oprávnění jeho stránek,

  • mapování objektu section s různým oprávněním (čtení, zápis, spouštění kódu, copy on write a jejich kombinace),

  • mapování objektu Section s různým oprávněním (čtení, zápis, spouštění kódu, copy on write a jejich kombinace) s pomocí velkých stránek. Kód předchozího testu mapoval pouze za využití malých stránek;

  • rezervování oblasti paměti pro mapování objektu Section,

  • přímé mapování fyzické paměti (mapování objektu DevicePhysicalMemory),

  • mapování fyzické paměti prostřednictvím rozhraní AWE,

  • rezervování bloku virtuální paměti pro mapování pomocí rozhraní AWE,

  • alokace a zabezpečení oblasti privátní paměti. Kód testu alokuje a zabezpečí dvě oblasti: jednu s oprávněním jen pro čtení, druhou s oprávněním pro čtení i zápis,

  • alokace privátní paměti pro čtení i zápis a její zabezpečení pouze pro čtení,

  • alokace oblastí privátní paměti s oprávněním jen pro čtení a pro čtení i zápis a zabezpečení pouze části těchto oblastí. Při zabezpečení je použito stejné oprávnění jako při alokaci;

  • alokace oblastí privátní paměti s oprávněním jen pro čtení a pro čtení a zápis a zabezpečení několika separátních podoblastí v těchto oblastech. Při zabezpečení je použito stejné oprávnění jako při alokaci.

Physmem

Windows XP a Windows Server 2003 SP0 umožňovaly aplikacím běžícím s administrátorským oprávněním přímo přistupovat k objektu DevicePhysicalMemory, který reprezentuje fyzickou paměť počítače. Výchozí nastavení zabezpečení objektu dovolovalo těmto aplikacím fyzickou paměť pouze číst, administrátorská oprávnění umožňovala toto omezení odstranit, čímž aplikace získala prakticky neomezený přístup k obsahu celé fyzické paměti.

Přepis fyzické paměti přestavoval mimo jiné jednu z cest do jádra operačního systému, a tak přístupy k objektu DevicePhysicalMemory začalo hlídat mnoho bezpečnostních aplikací. Vývojáři Microsoftu se rozhodli v aktualizaci Service Pack 1 pro Windows Server 2003 přístup k tomuto objektu zakázat z uživatelského režimu úplně, což představuje dle mého názoru rozumný krok.

Z hlediska studia deskriptorů virtuálních adres je mapování obsahu fyzické paměti přes objekt DevicePhysicalMemory zajímavý tím, že takový blok virtuálních stránek jádro reprezentuje deskriptorem typu VadDevicePhysicalMemory. A protože tento seriál pojednává především o chování deskriptorů na Windows 7, potřeboval jsem fyzickou paměť nějakým způsobem mapovat do privátní části adresového prostoru procesu, abych tento deskriptor mohl pozorovat. Protože přístup k DevicePhysicalMemory je ve Windows 7 z uživatelského režimu nemožný, rozhodl jsem se k tomuto objektu dostat z režimu jádra.

A tak vznikl projekt Physmem. Skládá se s knihovny DLL, která poskytuje aplikacím rutiny pro mapování a odmapování fyzické paměti. Jednotlivým stránkám mapování nastavuje oprávnění jen pro čtení. Knihovna komunikuje pomocí dvou různých obecných zpráv (IOCTL) s ovladačem physmem.sys, který provádí požadované operace. Celý projekt je určen pouze pro testovací účely.

Knihovna nezajišťuje načtení ovladače do jádra operačního systému, toto břemeno přenechává aplikaci, která knihovnu používá, nebo libovolné jiné entitě.

Secmem

Secmem je knihovna, která si klade za cíl umožnit i obyčejným aplikacím běžícím v uživatelském režimu zabezpečovat oblasti svého adresového prostoru proti odstranění (uvolnění) a částečně také proti změně oprávnění. Celý projekt tvoří knihovna secmem.dll a ovladač secmem.sys. Knihovna komunikuje s ovladačem prostřednictvím dvou druhů obecných zpráv (IOCTL), které slouží jako příkaz k nastavení a odstranění zabezpečení ze zadané oblasti adresového prostoru. Ovladač zadanou oblast zabezpečuje voláním rutiny MmSecureVirtualMemory.

Knihovna secmem.dll nezajišťuje načtení ovladače secmem.sys do paměti jádra. Tuto akci musí zajistit aplikace, která knihovnu používá, nebo jiná entita.

Program VadTest v rámci několika svých testů využívá právě služeb knihovny secmem.dll, a tak nepřímo i ovladače secmem.sys.

Vad

Tento projekt patří do stejné skupiny nástrojů jako dříve představovaná utilita Vq. Disponuje jednoduchým grafickým uživatelským rozhraním, ve kterém zobrazuje seznam deskriptorů virtuálních adres popisujících adresový prostor cílového procesu. O každém deskriptoru zobrazuje počáteční a koncovou virtuální adresu popisované oblasti, typ struktury (krátký, dlouhý, normální), hodnoty jednotlivých příznaků a informace o zabezpečení. Jednotlivé skupiny informací jsou zobrazovány podle toho, zda jimi daná struktura deskriptoru disponuje a zda jsou přítomné na verzi Windows, na které je program Vad spuštěn.

Projekt Vad se skládá z ovladače vad.sys, který čte údaje z deskriptorů virtuálních adres cílového procesu, a aplikace vad.exe, jenž tyto informace zobrazuje. Původně tento projekt sloužil jako ukázkový příklad k mé knize. Od té doby se však dočkal několika vylepšení. Svět deskriptorů virtuálních adres mě začal velmi zajímat a vývoj tohoto projektu představoval jeden ze způsobů, jak se o této oblasti jádra dozvědět více.

Upozornění: Ovladač vad.sys při čtení dat z deskriptorů virtuálních adres neprovádí potřebné zamykání, takže může dojít k situaci, že mu jádro operačního systému právě zkoumaný deskriptor „pod rukama“ změní či dokonce úplně odstraní. Takové případy mohou končit až modrou obrazovkou smrti. V reálném nasazení jsem takové scénáře zatím nepozoroval. Ovladač pracuje pro testovací účely dostatečně spolehlivě. V budoucích verzích projektu nevylučuji přidat potřebné zamykání, aby se ovladač k deskriptorům choval stejným způsobem jako jádro operačního systému, alespoň pro novější verze Windows.

Definice struktur deskriptorů na starších verzích Windows

V této sekci naleznete definice struktur _MMVAD_XXX a záznamů, které tvoří jejich součásti, na starších verzích Windows. Pokud některá z definic zde chybí, shoduje se s tou uvedenou v hlavní části textu pro operační systémy Windows 7.

Windows Vista

typedef struct _MMVAD_SHORT {
  MMADDRESS_NODE Node;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID PushLock;
  union {
    ULONG_PTR LongFlags3;
    MMVAD_FLAGS3 VadFlags3;
  } u5;
} MMVAD_SHORT, *PMMVAD_SHORT;
 
typedef struct _MMVAD {
  MMADDRESS_NODE Node;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID PushLock;
  union {
    ULONG_PTR LongFlags3;
    MMVAD_FLAGS3 VadFlags3;
  } u5;
 
  union {
    ULONG LongFlags2;
    MMVAD_FLAGS2 VadFlags2;
  } u2;
 
  PVOID SubSection;
  PVOID MappedSubSection;
  PVOID FirstPrototypePte;
  PVOID LastContignuousPte;
} MMVAD, *PMMVAD;
 
typedef struct _MMVAD_LONG {
  MMADDRESS_NODE Node;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID PushLock;
  union {
    ULONG_PTR LongFlags3;
    MMVAD_FLAGS3 VadFlags3;
  } u5;
 
  union {
    ULONG LongFlags2;
    MMVAD_FLAGS2 VadFlags2;
  } u2;
 
  PVOID SubSection;
  PVOID FirstPrototypePte;
  PVOID LastContignuousPte;
  union {
    LIST_ENTRY SecureList;
    MMADDRESS_LIST Secured;
  } u3;
 
  union {
    PVOID Banked;
    PVOID ExtendedInfo;
  } u4;
} MMVAD_LONG, *PMMVAD_LONG;

Windows Server 2003 a Windows XP x64

typedef struct _MMVAD_SHORT {
  MMADDRESS_NODE Node;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
} MMVAD_SHORT, *PMMVAD_SHORT;
 
typedef struct _MMVAD {
  MMADDRESS_NODE Node;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID ControlArea;
  PVOID FirstPrototypePte;
  PVOID LastContiguousPte;
  union {
    ULONG LongFlags2;
    MMVAD_FLAGS2 VadFlags2;
  } u2;
} MMVAD, *PMMVAD;
 
typedef struct _MMVAD_LONG {
  MMADDRESS_NODE Node;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID ControlArea;
  PVOID FirstPrototypePte;
  PVOID LastContiguousPte;
  union {
    ULONG LongFlags2;
    MMVAD_FLAGS2 VadFlags2;
  } u2;
 
  union {
    LIST_ENTRY List;
    MMADDRESS_LIST Secured;
  } u3;
 
  union {
    PVOID Banked;
    PVOID ExtendedInfo;
  } u4;
} MMVAD_LONG, *PMMVAD_LONG;

Windows XP (32bit)

typedef struct _MMVAD_FLAGS {
  ULONG_PTR CommitCharge : 19;
  ULONG_PTR PhysicalMapping : 1;
  ULONG_PTR ImageMap : 1;
  ULONG_PTR UserPhysicalPages : 1;
  ULONG_PTR NoChange : 1;
  ULONG_PTR WriteWatch : 1;
  ULONG_PTR Protection : 5;
  ULONG_PTR LargePages : 1;
  ULONG_PTR MemCommit : 1;
  ULONG_PTR PrivateMemory : 1;
} MMVAD_FLAGS;
 
typedef struct _MMVAD_SHORT {
  ULONG_PTR StartingVpn;
  ULONG_PTR EndingVpn;
  PVOID Parent;
  PVOID LeftChild;
  PVOID RightChild;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
} MMVAD_SHORT, *PMMVAD_SHORT;
 
typedef struct _MMVAD {
  ULONG_PTR StartingVpn;
  ULONG_PTR EndingVpn;
  PVOID Parent;
  PVOID LeftChild;
  PVOID RightChild;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID ControlArea;
  PVOID FirstPrototypePte;
  PVOID LastContiguousPte;
  union {
    ULONG LongFlags2;
    MMVAD_FLAGS2 VadFlags2;
  } u2;
} MMVAD, *PMMVAD;
 
typedef struct _MMVAD_LONG {
  ULONG_PTR StartingVpn;
  ULONG_PTR EndingVpn;
  PVOID Parent;
  PVOID LeftChild;
  PVOID RightChild;
  union {
    ULONG_PTR LongFlags;
    MMVAD_FLAGS VadFlags;
  } u;
 
  PVOID ControlArea;
  PVOID FirstPrototypePte;
  PVOID LastContiguousPte;
  union {
    ULONG LongFlags2;
    MMVAD_FLAGS2 VadFlags2;
  } u2;
 
  union {
    LIST_ENTRY List;
    MMADDRESS_LIST Secured;
  } u3;
 
  union {
    PVOID Banked;
    PVOID ExtendedInfo;
  } u4;
} MMVAD_LONG, *PMMVAD_LONG;

Různé užitečné odkazy a zdroje informací

Leave a Reply

Your email address will not be published. Required fields are marked *