閱讀218 返回首頁    go 京東網上商城


Windows 核心編程研究係列之二:讀取指定物理內存地址中的內容

 

[原創/討論]Windows核心編程研究係列之二:

讀取指定物理內存地址中的內容

關鍵字:windows內核,物理內存

 

大家知道在windows NT中,如果已知虛擬地址可以通過

進程頁表或者內核提供的MmGetPhysicalAddress來取得對應的

物理地址。

現在我們反過來看一下,如果已知一個物理內存地 址 (假設地址有效),如何取得物理地址中的內容呢?經過一番思考後,我發現一個方法,但我這裏先賣個關子,為什麼呢?

因為我覺得這個方法有些繁瑣。經過和csdn的各位高手討論

之後,加上本人的一共總結出3種方法,現在想和大家分享一下,

下麵不再廢話,立即進入正題J

 

[方法1]:使用內核提供的MmMapIoSpace函數

原來內核早就提供了很簡單的接口,就是MmMapIoSpace函數,不過在DDK文檔中看到該函數的說明如下:

 

PVOID MmMapIoSpace(

   IN PHYSICAL_ADDRESS  PhysicalAddress,

   IN ULONG  NumberOfBytes,

IN MEMORY_CACHING_TYPE CacheType );

 

它隻有3個形參,這和實際Masm32v9.0聲明的4個形參有所

不同,為了確定,我通過 IDA反匯編證實該函數確實有4個形參。經過測試,我猜測中間的ULONG NumberOfBytes參數實際由64位字節的(兩個雙字)高低兩部分組成,即在Masm32v9.0中有:

 

PVOID MmMapIoSpace(

   IN PHYSICAL_ADDRESS  PhysicalAddress,

IN ULONG NumberOfBytesHighPart,

IN ULONG NumberOfBytesLowPart,

IN MEMORY_CACHING_TYPE CacheType );

 

這樣的話調用就很簡單了:

invoke  MmMapIoSpace,_phyaddr,0,4,MmNonCached

若成功該函數返回影射後的虛擬地址,否則返回NULL。這樣就可以間接達到讀取物理地址中內容的第一個方法。但可能我對參數功能的猜測也有錯誤,如果有誤請指出。不勝感謝。

 

 

[方法2]:通過操作物理內存對象來完成到虛擬地址的影射

簡單的說這個方法的基本思想如下:

 

a :取得本進程句柄;

b :取得物理內存對象的句柄;

c :將物理內存地址影射到進程的虛擬地址空間中。

 

下麵是我從C改寫後的匯編關鍵代碼:

 

;***************************************************************************

GetValByPhyAddr  proc uses esi edi ebx _phyaddr

     local   hpmem:dword

     local   hp:dword

     local   dwsize:dword

     local   dwbase:dword

     local   dwret:dword

     local   cid:CLIENT_ID

     local   stLR:LARGE_INTEGER

     local   ObjAttrsMem:OBJECT_ATTRIBUTES

     local   ObjAttrsP:OBJECT_ATTRIBUTES

 

     mov    dwbase,0

     mov    dwsize,4

     push   _phyaddr

     pop     stLR.LowPart

     mov    stLR.HighPart,0

 

     invoke PsGetCurrentProcessId

     mov    cid.UniqueProcess,eax

     mov    cid.UniqueThread,0

 

     invoke InitObjAttrs,addr ObjAttrsP,0

     ;取得本進程句柄

     Invoke      ZwOpenProcess,addr hp,PROCESS_DUP_HANDLE,/

                 addr ObjAttrsP,addr cid

 

     invoke InitObjAttrs,addr ObjAttrsMem,addr devphymem

 

     ;取得物理對象的句柄

     Invoke     ZwOpenSection,addr hpmem,/

                     SECTION_MAP_READ or SECTION_MAP_WRITE,/

                     addr ObjAttrsMem

 

     ;將物理內存地址影射到進程的虛擬地址空間中

     

 

 

invoke      ZwMapViewOfSection,hpmem,hp,addr dwbase,/

                     0,4,addr stLR,addr dwsize,1,/

                     MEM_TOP_DOWN,PAGE_READWRITE

 

mov   edx,dwbase

     mov    eax,[edx]  ;取得影射後對應虛擬地址中的內容

     mov    dwret,eax

 

     ;釋放資源

     invoke ZwUnmapViewOfSection,hp,addr dwbase

     invoke ZwClose,hpmem

     invoke ZwClose,hp

 

     mov    eax,dwret

     ret

GetValByPhyAddr  endp

;***************************************************************************

 

 

[方法3]:關閉CPU分頁機製達到直接讀取物理地址的目的

 

 

3種方法就是我前麵賣關子的方法J ,這種方法不依賴於OS,了解保護模式的朋友都知道:進入保護模式後如果關閉分頁機製則CPU就會將線形地址直接解釋成物理地址。

 

關閉分頁很簡單,隻需3行匯編代碼:

 

mov    eax,cr0

and    eax,7fffffffh

mov    cr0,eax

 

打開分頁同樣很簡單:

 

mov eax,cr0

oreax,80000000h  

mov cr0,eax

 

既然這麼簡單,為什麼我在前麵說這個方法有些繁瑣呢?原因之一是如果僅僅關閉分頁就會使原來依賴於分頁的windows變得一團糟,馬上會發生著名的藍屏現象,不爽哦!L

那要怎麼做呢?經過我的嚐試,發現有3點非常重要:

 

 

1 :要保證windows處在較高的IRQL級別,防止關閉分頁後

       被打斷;

2 :要將處理分頁、讀取物理地址的指令塊放到線形地址等於

       物理地址的頁中;

3 :要做好數據的恢複工作。

 

1點和第3點都容易理解,可能有人會疑問第2點,為什麼要放到線形地址等於物理地址的空間中呢?原因其實也很簡單,就是一旦關閉分頁,所有地址都將以物理地址來解釋,如果先前的

虛擬地址和物理地址不相同那麼很可能就會發生執行錯誤指令流的問題。(當然也可以不相同,隻要你能保證關分頁後能執行正確指令即可。)

 

那麼如何將物理地址和虛擬地址設置為相同呢?我的方法是將虛擬地址對應地頁表PTE中的物理頁基址改為包容即可。

我隨機選擇了一個虛擬地址 0x1c00000 ,為了簡單它正好是頁麵的一個基址。下麵我構造了它的PTE使得其中的物理也幀指向它:

 

mov   edx,0ffdf0000h

     mov    eax,[edx]

     mov    tmp0,eax            ; 保存原始值

     ;mov   dword ptr [edx],1c00027h

     mov    dword ptr [edx],1c00007h

 

     ;初始化PTE

     mov    edx,0c03ff7c0h

     mov    eax,[edx]

     and     eax,0fffff000h

     or  eax,7h

     mov    edx,0c030001ch

     mov    ebx,[edx]

     mov    tmp1,ebx                  ; 保存原始值

     mov    dword ptr [edx],eax

     ;刷新TLB

     mov    eax,cr3

     mov    cr3,eax

 

           最後兩句指令在我前一篇: <<Windows內核編程研究一:改變進程PTE>>中有過說明,這裏就不解釋了。

 

結束了PTE的設置後,下麵就是將指令塊拷貝到指定位置,代碼如下:

 

mov   ecx,slen

mov   esi,subasm

mov   edi,1c00000h

cld

rep           movsb

 

subasm指令塊完成的就是實際設置分頁和讀取物理地址內容的工作,如下:

 

subasm:

           mov     eax,cr0

           and     eax,7fffffffh               ;關閉分頁機製

           mov     cr0,eax    

           jmp     SHORT PageC

PageC:

           mov    edx,[ecx]

 

mov   eax,cr0

           or  eax,80000000h            ;打開分頁機製

           mov    cr0,eax

          

jmp    SHORT PageO

PageO:

          

jmp    edi

slen   =   $ - subasm

 

 

借用一句話作為結尾“一切都那麼清楚明白,然而卻無法透徹理解。理解就是改變,就是超越自己已有的認識。”

感謝CSDN熱心朋友提供的方法和意見,給我很好的啟發。

       末尾,感謝各位觀賞,如有誤謬之處請不吝指出,不勝感謝。希望有同好的朋友能夠一起討論,一起交流。

 

Email:  19796174@qq.com

QQ    19796174

 

最後“預告”一下我的第3篇文章:

<<Windows核心編程研究係列之三:突破windows共享文件打開限製>>

 

主要內容:

很多用No_Share打開的文件,正常情況下不能在其他進程用

CreateFile打開,或者是僅僅用FILE_SHARE_READ打開的

文件不能再以寫入目而打開,那麼如何繞過windows的這種保護呢?敬請期待。J

 

感謝觀賞,拜拜!

 

 

Mydo(侯佩|hopy|福信克|FreeThink)

20061219晚於合肥溫馨的家中J

 

最後更新:2017-04-02 00:00:28

  上一篇:go 調試
  下一篇:go 麵試問題之 按單詞反轉字符串