2011年7月28日星期四

讓XP用上4G內存

作 者: scdey
時 間: 2011-07-28, 00:13:40
鏈接: http://bbs.pediy.com//showthread.php?t=137830

先上圖,patch之後的,原來只有2.98G,現在是3.86G了

去年7月入手小黑T410,到手就裝了4G的內存,WINDOWS 7的破解補丁來的很快,很順利就用上了3.86G(糾
結於剩下的140M哪裡去了?至今也沒搞明白,只知道主板沒有映射),而所謂的XP的種種補丁,不外乎就是
Ramdisk,開啟PAE之類的,毫無用處,最可憐的是竟然被某網友的“開啟了PAE就能用到全部4G內存,系統
屬性頁顯示還是2.98G是假的”一說給忽悠了一年,沒文化真可怕。。。就這麼將信將疑用了一年,中間也
糾結過一段時間,沒有深入分析,近日越想越感覺不對勁,再來糾結糾結。 。 。
用WinDbg看看

代碼:
lkd> dd MmHighestPhysicalPage
8088b124 000bf7ff 000bf399 00000040 00000000
lkd> dd MmNumberOfPhysicalPages
8088b128 000bf399 00000040 00000000 7fff0000
可見最高物理內存頁號MmHighestPhysicalPage值為bf7ff,物理內存總頁數MmNumberOfPhysicalPages值為
bf399,換算成物理內存數0xbf399*0x1000=2.98G正好是系統屬性頁顯示的2.98G,改變這個值,系統屬性頁
的值也會跟著變,是不是把這個值改了你就能用到更多的內存了呢,當然不是,任務管理器裡記錄的內存使
用量確是真是的。
那是不是我的PAE沒有真正啟用呢?
那我們再用WinDbg看看

代碼:
lkd> !pte 80800000
                    VA 80800000
PDE at C0602020 PTE at C0404000
contains 00000000008009E3 contains 0000000000000000
pfn 800 -GLDA--KWEV LARGE PAGE pfn 800
看吧,PDE和PTE裡面的物理地址00000000008009E3和0000000000000000都是64位的,而在沒有啟用PAE的系
統裡,頁表項裡的物理地址是32位的。 (為什麼PTE裡是一串0呢?因為我們看的80800000這個虛擬地址是
ntkrnlpa.exe的基地址,它當然是加載在物理內存的0地址的)
那麼是不是系統偷偷地在用我的4G內存了,而給我顯示出2.96G的假象呢?
再祭出我們的法寶WinDbg

代碼:
lkd> dd poi(MmPhysicalMemoryBlock)
8ad75c80 00000007 000bf3ab 00000001 0000009d
8ad75c90 00000100 000bf17c 000bf282 000000dd
8ad75ca0 000bf40f 00000060 000bf70f 00000008
8ad75cb0 000bf71f 0000004c 000bf7ff 00000001
這裡有兩個結構體:

代碼:
typedef struct _PHYSICAL_MEMORY_RUN {
    PFN_NUMBER BasePage;
    PFN_NUMBER PageCount;
} PHYSICAL_MEMORY_RUN, *PPHYSICAL_MEMORY_RUN;

typedef struct _PHYSICAL_MEMORY_DESCRIPTOR {
    ULONG NumberOfRuns;
    PFN_NUMBER NumberOfPages;
    PHYSICAL_MEMORY_RUN Run[1];
} PHYSICAL_MEMORY_DESCRIPTOR, *PPHYSICAL_MEMORY_DESCRIPTOR;
從上面可以看出,我的機器有7塊可用的內存,總共有bf3ab頁(為什麼這個數字跟上面看到的
MmNumberOfPhysicalPages不符呢? ),分別為1 -9d,100- bf17c,…,bf7ff,可見最大物理內存地址為
bf7ff,還是與4G內存相去甚遠啊。 。 。
那麼是不是我的主板根本就不識別這麼大的內存呢? ?
那麼我們再做做實驗,用nt4的源代碼編譯一份NTLDR,把osloader.exe探測到的物理內存輸出一份,下面是
通過中斷獲取的內存佈局圖,BIOS專家們都叫把它做E820圖

代碼:
Base Size Type
0 9E800 1
9E800 1800 2
D2000 2000 2
DC000 24000 2
100000 BF17C000 1
BF27C000 6000 2
BF282000 DD000 1
BF35F000 12000 2
BF371000 1000 4
BF3F2000 1D000 2
BF40F000 60000 1
BF46F000 1F9000 2
BF668000 80000 4
BF6E8000 27000 2
BF717000 8000 1
BF71F000 4C000 1
BF76B000 C000 4
BF777000 3000 3
BF77A000 7000 4
BF781000 1000 3
BF782000 9000 4
BF78B000 1000 3
BF78C000 13000 4
BF79F000 60000 3
BF7FF000 1000 1
BF800000 800000 2
E0000000 10000000 2
FEAFF000 1000 2
FEC00000 10000 2
FED00000 400 2
FED1C000 4000 2
FED20000 70000 2
FEE00000 1000 2
FF000000 1000000 2
100000000 38000000 1
圖中type為1的就是分配給本機物理內存的地址,其他的為其他硬件所用,我們把內存地址挑出來:

代碼:
Base Size
0 9E800
100000 BF17C000
BF282000 DD000
BF40F000 60000
BF717000 8000
BF71F000 4C000
BF7FF000 1000
100000000 38000000
總數為F73AC800=3.86G,儘管4G以下的地址空間被硬件佔用了不少,主板並沒有放棄那塊內存嘛,只是把他
們映射到了4G以上的空間,即100000000-138000000,看來是XP那傢伙太不地道,活生生把咱們使用這塊內
存給掐掉了,故而產生了MmHighestPhysicalPage= BF7FF,無恥的傢伙!什麼? XP的內存機制不支持?不要
為XP辯解,PAE技術早在Intel P6家族的CPU身上就已經開始使用了,Intel手冊第一卷3.3.6節關於PAE有如
下表述:
Beginning with P6 family processors, the IA-32 architecture supports addressing of
up to 64 GBytes (2^36 bytes) of physical memory.
也就是說,從Intel P6家族的CPU開始,(PAE技術讓)IA-32架構的CPU就支持對64G的物理內存進行尋址,
P6家族可是很老CPU了,奔二,奔三就屬於P6家族的,所以XP這個後來才發布的操作系統不可能連PAE都沒考
慮進去吧。
既然那塊4G以上的內存地址被主板識別,NTLDR也探測到了,操作系統也支持,那我們為什麼還是用不到
呢?到底是NTLDR沒有告訴ntkrnlpa.exe,還是ntkrnlpa.exe自己給我們截斷了?
這怎麼調試呢? Bochs?不行,我總共才4G內存,哪有那麼多內存分配給Bochs用呢,要有XP的ntos的源碼就
好了,ntos的入口函數為
VOID KiSystemStartup(PLOADER_PARAMETER_BLOCK KissLoaderBlock)
在NTLDR向ntos交權的時候,會將內存描述鍊錶通過結構體參數LOADER_PARAMETER_BLOCK傳過去,這個結構
體原型為

代碼:
typedef struct _LOADER_PARAMETER_BLOCK {
    LIST_ENTRY LoadOrderListHead;
    LIST_ENTRY MemoryDe​​scriptorListHead;
    LIST_ENTRY BootDriverListHead;
ULONG KernelStack;
……
後面太長,省略掉
} LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;
既然沒有XP的源碼,那就用wrk將就一下吧,將wrk編譯的內核文件wrkx86.exe來替換ntkrnlpa.exe,系統肯
定是起不來的,不過我們只需要在wrkx86.exe的入口點打印出NTLDR傳過來的內存描述鍊錶就好了,
MEMORY_ALLOCATION_DESCRIPTOR的原型為

代碼:
typedef struct _MEMORY_ALLOCATION_DESCRIPTOR {
    LIST_ENTRY ListEntry;
    TYPE_OF_MEMORY MemoryType;
    ULONG BasePage;
    ULONG PageCount;
} MEMORY_ALLOCATION_DESCRIPTOR, *PMEMORY_ALLOCATION_DESCRIPTOR;
於是我們很快得到了結果:(無法上圖,遺憾)

咦! NTLDR貌似真的沒有把4G以上的地址傳過來啊,怎麼到FF000這塊內存就完了呢?
難道NTLDR私自把4G以上的地址給裁了?難道一切罪惡的源泉在NTLDR?在此我犯了個嚴重的錯誤,以為勝利
在望,加班加點研究NTLDR,最好成功跳過NTLDR截去4G以上內存的代碼了,啟動發現XP依然顯示2.98G的可
用內存,怎麼回事呢?回過頭來再分析NTLDR,才發現瞭如下的代碼

代碼:
if ( (_BYTE)BlUsePae_0 )
    {
      v10 = BlpAllocatePAETables();
      if ( v10 )
        return v10;
    }
    else
    {
      BlpTruncateDescriptors(0xFFFFFu);
}
BlpTruncateDescriptors(0xFFFFFu)函數的功能就是設置內存描述鍊錶的最大頁面號為0xFFFFF,即截去4G
以上的內存,原來我們編譯的wrkx86.exe不支持PAE,被NTLDR發現了,故而才調用BlpTruncateDescriptors
截斷的,而我們的XP用的內核ntkrnlpa.exe是支持PAE的,那麼就不會截斷了,哎,馬虎啊。 。 。
那還是鎖定ntkrnlpa.exe分析吧,充分發揚廢寢忘食的精神,終於找到了一個可疑的函數ExVerifySuite,
這不會就是驗證我們版本的函數吧,網上一查,發現有位“老生常談”早就發現了,他的文章在這裡
http://thxlp.wordpress.com/2008/08/03/%E8%80%81%E7%94%9F%E5%B8%B8%E8%B0%88-
windows%E5%92%8C4g%E4%BB%A5%E4%B8%8A%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98/
汗。 。 。 。差距啊,不過這位老大發現這麼久竟然不出補丁,拯救我們廣大百姓於水貨,哎。 。 。害我熬夜
傷神這麼久。 。 。 。
不過這位“老生”的代碼不知道從哪裡搞的,nt4源碼裡沒有MiCheckPaeLicense這個函數,而windows2000
的源代碼裡雖然有這個函數,但差別很大,wrk的源代碼裡也不是那樣的,反彙編XP的ntkrnlpa.exe,代碼
如下

代碼:
int __usercall MiCheckPaeLicense<eax>(PLOADER_PARAMETER_BLOCK LoaderBlock<eax>)
{
  EndPage = 0;
  LoaderBlock1 = LoaderBlock;
  MaxPageCount = 0x100000u;
  MaxPage = 0;
  if ( ExVerifySuite(DataCenter) == 1 )
  {
    if (LoaderBlock->u.I386.VirtualBias )
    {
      MaxPageCount = 0x400000u; // booting /3gb: 16G
      MaxPage = 0x400000u;
    }
    else
    {
      MaxPageCount = 0x1000000u;
    } // DataCenter: 64G
  }
  else
  {
    if ( MmProductType == 0x690057 || ExVerifySuite(Enterprise) != 1 )
    {
      if ( ExVerifySuite(ServerAppliance) == 1 )
        MaxPageCount = 0x80000u; // 2G
      else
        MaxPage = 0x100000u; // 4G
    }
    else
    {
      MaxPageCount = 0x800000u;// Advanced Server is permitted a maximum of 32gb
    }
  }
實在是不知道這位高人的代碼來自哪裡,懇請各位高人給予指點。 。 。
從這段代碼裡可以看出,MiCheckPaeLicense函數會檢查操作系統的版本,如果是DataCenter,就允許使用
64G內存,Advanced Server為32G,如果為精簡版則為2G,其他版本為4G,看來真是ntkrnlpa.exe在作怪,
先別急著patch,驗證內存限制的還有一個地方,在MmAddPhysicalMemoryEx函數里也會調用ExVerifySuite
這個函數,代碼如下:

代碼:
if ( ExVerifySuite(DataCenter) == 1 )
  {
    LimitPage = 0x1000000u; // DataCenter : 64G
  }
  else
  {
    if ( MmProductType == 0x690057 || (v9 = ExVerifySuite(Enterprise) == 1, LimitPage =
0x800000u, !v9) )// Advanced Server : 32G
      LimitPage = 0x100000u; // Other : 4G
  }
代碼都差不多,要patch的話兩個地方要一起改,至於怎麼改?代碼都在這麼裡,想怎麼改就怎麼改吧,只
要兩個地方都改了就行,只要其中一個地方不改,ntos都會陰魂不散的把你多出來的內存吃掉…
看成果吧

代碼:
lkd> dd MmHighestPhysicalPage
8088b124 00137fff 000f7399 00000040 00000000

lkd> dd MmNumberOfPhysicalPages
8088b128 000f7399 00000040 00000000 7fff0000

lkd> !pte d0800000
                    VA d0800000
PDE at C0603420 PTE at C0684000
contains 00000001004DF963 contains E15C080000000400
pfn 1004df -G-DA--KWEV not valid
                            Proto: E15C0800
數數這個地址1004DF963,9位啊,4G以上了,不要被E15C080000000400這個地址嚇到了,64位,有這麼大的
地址嗎?查查PTE的結構體就知道了,前面的幾位是標誌位
再看物理內存塊

代碼:
lkd> dd poi(MmPhysicalMemoryBlock)
8baa3c70 00000008 000f73ab 00000001 0000009d
8baa3c80 00000100 000bf17c 000bf282 000000dd
8baa3c90 000bf40f 00000060 000bf70f 00000008
8baa3ca0 000bf71f 0000004c 000bf7ff 00000001
8baa3cb0 00100000 00038000 0001000a 6c4d6d4d
看到了嗎,我的機器現在有8塊可用的內存了,多了一個100000-138000,總共有f73ab頁了,
0xf73ab*0x1000=3.86G,至於還有140M,主板沒映射,用不到了,能不能解決呢?希望各位牛人能夠給予指
點。 。 。

後面部分比較倉促,主要是聽到重任到來的消息,沒時間仔細寫了,補丁沒寫完,估計一時半會完成不了
了,倉促發帖,開始潛伏。 。 。

没有评论:

发表评论