[原創/讨論]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中有:
in ulong numberofbyteshighpart,
in ulong numberofbyteslowpart,
這樣的話調用就很簡單了:
invoke mmmapiospace,_phyaddr,0,4,mmnoncached
若成功該函數傳回影射後的虛拟位址,否則傳回null。這樣就可以間接達到讀取實體位址中内容的第一個方法。但可能我對參數功能的猜測也有錯誤,如果有誤請指出。不勝感謝。
[方法2]:通過操作實體記憶體對象來完成到虛拟位址的影射
簡單的說這個方法的基本思想如下:
a :取得本程序句柄;
b :取得實體記憶體對象的句柄;
c :将實體記憶體位址影射到程序的虛拟位址空間中。
下面是我從c改寫後的彙編關鍵代碼:
;***************************************************************************
getvalbyphyaddr proc uses esi edi ebx _phyaddr
local
hpmem:dword
hp:dword
dwsize:dword
dwbase:dword
dwret:dword
cid:client_id
stlr:large_integer
objattrsmem:object_attributes
objattrsp:object_attributes
mov
dwbase,0
dwsize,4
push
_phyaddr
pop
stlr.lowpart
stlr.highpart,0
invoke
psgetcurrentprocessid
cid.uniqueprocess,eax
cid.uniquethread,0
initobjattrs,addr objattrsp,0
;取得本程序句柄
invoke
zwopenprocess,addr hp,process_dup_handle,/
addr objattrsp,addr cid
initobjattrs,addr objattrsmem,addr devphymem
;取得實體對象的句柄
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
eax,[edx] ;取得影射後對應虛拟位址中的内容
dwret,eax
;釋放資源
zwunmapviewofsection,hp,addr dwbase
zwclose,hpmem
zwclose,hp
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
eax,[edx]
tmp0,eax ; 儲存原始值
;mov
dword ptr [edx],1c00027h
dword ptr [edx],1c00007h
;初始化pte
edx,0c03ff7c0h
and
eax,0fffff000h
or
eax,7h
edx,0c030001ch
ebx,[edx]
tmp1,ebx ; 儲存原始值
dword ptr [edx],eax
;重新整理tlb
eax,cr3
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: [email protected]
qq:
19796174
最後“預告”一下我的第3篇文章:
<<windows核心程式設計研究系列之三:突破windows共享檔案打開限制>>
主要内容:
很多用no_share打開的檔案,正常情況下不能在其他程序用
createfile打開,或者是僅僅用file_share_read打開的
檔案不能再以寫入目而打開,那麼如何繞過windows的這種保護呢?敬請期待。j
感謝觀賞,拜拜!
mydo(侯佩|hopy|福信克|freethink)
2006年12月19日晚于合肥溫馨的家中j