習一下利用修改物理內存來跨進程內存讀寫
系統:win10 21h1 x64
編譯環境: vs2022 詳情見附錄
基礎
虛擬地址轉物理地址
虛擬地址也稱線性地址,一個線性地址+進程的DirBase地址可以轉換成物理地址。先來看線性地址的含義
在x64體系中只實現了48位的virtual address,高16位被用作符號擴展,這高16位要么全是0,要么全是1。
不同于x86體系結構,每級頁表尋址長度變成9位,由于在x64體系結構中,普通頁大小仍為4KB,然而數據卻表示64位長,因此一個4KB頁在x64體系結構下只能包含512項內容,所以為了保證頁對齊和以頁為單位的頁表內容換入換出,在x64下每級頁表尋址部分長度定位9位。
?
從Page Map Level 4(PML4)開始到最后的物理地址,每一個都可以理解成一層頁表的索引,索引值就是線性地址上不同的部分,分別縮寫是PML4, PDPE, PDE,PTE。?
?
使用windbg可以先查看進程對應的DirBase地址,然后再使用!vtop Dirbase地址 虛擬地址
查看虛擬地址對應的物理地址,如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
上面得到DirBase的值是235ae6000
,然后需要查看物理地址的虛擬地址是0x0000A6E334FB00
,就使用命令
1 |
|
得到最后對應的物理地址是0x11b10cb00。
簡單例子代碼如下:
1 2 3 4 5 6 7 8 9 10 11 |
|
運行后可以打印出來字符串的虛擬地址0000A6E334FB00
,然后通過上述步驟得到物理地址。
我們嘗試看看物理內存中的字符串,現在已經確定物理內存的地址是0xD0000147
,使用!db 0xD0000147
來查看物理內存,記住要!
,沒有感嘆號的是查看虛擬內存的
1 2 3 4 5 6 7 8 9 |
|
可以看到物理內存上的字符串內容。
DirBase地址獲取
DirBase地址除了通過上述windbg直接得到這個值以外,還可以通過EPROCESS來得到,這個是代碼比較需要的
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
DirectoryTableBase的值就是DirBase地址了,實際上就是EPROCESS + 0x28的偏移
還可以通過獲取CR3寄存器的值,CR3寄存器中的值就是頁目錄表的物理地址,也就是DirBase
思路
目的:進程B可以通過修改物理內存的內容來修改進程A內存中的數據
實驗設置:進程A泄露一個變量地址,然后等待進程B修改,修改后再回復執行,打印變量值看是否修改成功
內核部分思路:
- 將R3的虛擬地址轉換為物理地址
- 使用MmCopyMemory復制物理地址內容
- 修改內容
- 使用mmMapIoSpaceEx將修改后的內容映射回物理地址
代碼實現
被修改進程代碼
這里寫一個例子來充當被攻擊(修改內存)的進程。主要就是打印變量內容和地址,然后暫停程序等待一段時間(等待被驅動修改),然后再打印變量內容,看看是否被驅動修改內存成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
驅動代碼
這里就是主要邏輯,通過驅動代碼取修改目標進程的內存內容,做到跨進程內存讀取,修改。
定義一個讀取物理內存函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
再定義一個寫入物理內存的函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
我們需要將虛擬地址轉換成物理地址,那么首先需要線性地址+DirBase地址,DirBase地址獲取是通過PEPROCESS+0x28偏移讀取的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
得到DirBase后,就可以虛擬地址轉換物理地址。
傳入虛擬地址后,取后48bit,然后將這48bit分成4個9bit和最后12bit,分別是PML4,PDPE,PDE,PTE和頁內偏移offset。需要注意的是DirBase就已經是物理內存了,所以讀取DirBase內容并且一層一層讀取都要用自定義函數ReadPhysicalAddress。
每一層都是基地址+8*偏移,讀取的內容,取12-35bit就是下一層的基地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
最后再主函數中定義一下邏輯。這里直接手動指定進程號和目標進程打印出來的變量地址,然后將虛擬地址轉化成物理地址,讀取物理地址上的內容并打印出來看看是否正確。再修改物理地址上的內容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
結果
目標進程
?
可以看到目標進程的指定內存被修改,同時驅動也跨進程讀取,修改內存成功?