进程线程007 进程挂靠与跨进程读写内存
文章目錄
- 進(jìn)程掛靠
- 進(jìn)程與線程的關(guān)系
- 線程與進(jìn)程如何關(guān)聯(lián)
- 為什么需要ApcState.Process
- CR3的值可以隨便改嗎
- 分析NtReadVirtualMemory函數(shù)
- 總結(jié)
- 跨進(jìn)程讀寫內(nèi)存
- 跨進(jìn)程操作
- NtReadVirtualMemory流程解析
進(jìn)程掛靠
進(jìn)程與線程的關(guān)系
一個進(jìn)程可以包含多個線程
一個進(jìn)程至少要有一個線程
進(jìn)程為線程提供資源,也就是提供CR3的值,CR3中存儲的是頁目錄表的基址,CR3確定了線程能訪問的內(nèi)存也就確定了
mov eax,dowrd ptr ds:[0x12345678]CPU如何解析0x12345678這個地址呢?
線程與進(jìn)程如何關(guān)聯(lián)
ETHREAD結(jié)構(gòu)體:+0x034 ApcState+0x000 ApcListHead +0x010 Process +0x014 KernelApcInProgress+0x015 KernelApcPending+0x016 UserApcPending+0x220 ThreadsProcessETHREAD結(jié)構(gòu)體+0x220的位置存儲的就是當(dāng)前線程所屬的進(jìn)程。
另外在KTHREAD結(jié)構(gòu)體0x34的位置是子結(jié)構(gòu)體ApcState,ApcState也有一個成員Process指向了當(dāng)前線程所屬的進(jìn)程。
這就存在一個問題,同一個線程結(jié)構(gòu)體里存了兩份指針,這兩份指針代表什么?
為什么需要ApcState.Process
下面分析SwapContext函數(shù):
這里首先取出目標(biāo)線程的ApcState.Process存到eax里,然后比較當(dāng)前線程的ApcState.Process和目標(biāo)線程的這個成員是否相同,如果不相同就說明不屬于同一個進(jìn)程
代碼繼續(xù)往下走,就會切換CR3的值
線程切換的時候,會比較KTHREAD結(jié)構(gòu)體0x044處指定的EPROCESS是否為同一個,如果不是同一個,會將eax的值取出,賦給CR3。eax此時存儲的是目標(biāo)線程的ApcState.Process。這個時候就發(fā)生了進(jìn)程切換
所以,線程需要的CR3的值來源于0x44處偏移指定的EPROCESS
總結(jié):
0x220親生父母:這個線程誰創(chuàng)建的
0x44 養(yǎng)父母:誰在為這個線程提供資源,也就是提供CR3
一般情況下,0x220與0x44指向的是同一個進(jìn)程
CR3的值可以隨便改嗎
正常情況下,CR3的值是由養(yǎng)父母提供的,但CR3的值也可以改成和當(dāng)前線程毫不相關(guān)的其他進(jìn)程的DirectoryTableBase
線程代碼:
mov cr3,A.DirectoryTableBase mov eax,dword ptr ds:[0x12345678] //A進(jìn)程的0x12345678內(nèi)存 mov cr3,B.DirectoryTableBase mov eax,dword ptr ds:[0x12345678] //B進(jìn)程的0x12345678內(nèi)存 mov cr3,C.DirectoryTableBase mov eax,dword ptr ds:[0x12345678] //C進(jìn)程的0x12345678內(nèi)存將當(dāng)前cr3的值改為其他進(jìn)程,稱為進(jìn)程掛靠
分析NtReadVirtualMemory函數(shù)
接下來就通過分析NtReadVirtualMemory函數(shù),來看看是怎么讀取其他進(jìn)程的內(nèi)存。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-hprSWzoq-1581511027410)(assets/1581507589446.png)]
首先找到NtReadVirtualMemory函數(shù)
這個函數(shù)在內(nèi)部調(diào)用了MmCopyVirtualMemory,繼續(xù)跟進(jìn)
真正COPY的函數(shù)是MiDoMappedCopy,繼續(xù)跟進(jìn)
在開始讀取之前先調(diào)用了KeStackAttachProcess,也就是之前說過的進(jìn)程掛靠,繼續(xù)
這個函數(shù)又繼續(xù)調(diào)用了KiAttachProcess,繼續(xù)跟進(jìn)
這里先將該線程的+0x44位置的ApcState.Process修改為要讀取的進(jìn)程的KPROCESS
然后又調(diào)用KiSwapProcess,真正的掛靠是通過這個函數(shù)實現(xiàn)的,繼續(xù)跟進(jìn)
真正關(guān)鍵的代碼是上面兩行,首先取出要讀取進(jìn)程的CR3,+0x18的位置是DirectoryTableBase頁目錄表基址,然后修改CR3為要讀取進(jìn)程的CR3
NtReadVirtualMemory流程總結(jié)
既然修改CR3就可以讀取目標(biāo)進(jìn)程的內(nèi)存,那么NtReadVirtualMemory可不可以只修改CR3,不修改當(dāng)前線程的ApcState.Process為要掛靠的進(jìn)程。
答案是不可以。
回顧一下之前的為什么需要ApcState.Process的問題就會發(fā)現(xiàn),當(dāng)調(diào)用SwapContext進(jìn)行線程切換的時候,給CR3賦值的時候賦的是ApcState.Process的值。
如果沒有修改ApcState.Process,那就意味著ApcState.Process指向的不是掛靠的進(jìn)程,而是自己的父進(jìn)程,一旦這個時候發(fā)生線程切換并且在線程切換回來的時候,NtReadVirtualMemory讀取的就是自己進(jìn)程的內(nèi)存了。
如果我們自己來寫這個代碼,在切換CR3后關(guān)閉中斷,并且不調(diào)用會導(dǎo)致線程切換的API,就可以不用修改養(yǎng)父母的值
總結(jié)
正常情況下,當(dāng)前線程使用的CR3是由其所屬進(jìn)程提供的(ETHREAD 0x44偏移處指定的EPROCESS),正因為如此,A進(jìn)程中的線程只能訪問A進(jìn)程的內(nèi)存
如果要讓A進(jìn)程中的內(nèi)容能夠訪問B進(jìn)程的內(nèi)存,就必須修改CR3的值為B進(jìn)程的頁目錄表基址(DirectoryTableBase),這就是所謂的進(jìn)程掛靠
跨進(jìn)程讀寫內(nèi)存
跨進(jìn)程的本質(zhì)就是進(jìn)程掛靠,也就是修改CR3的值為目標(biāo)進(jìn)程的頁目錄表基址
跨進(jìn)程操作
A進(jìn)程中的線程代碼
mov cr3,B.DirectoryTableBase //切換Cr3的值為B進(jìn)程 mov eax,dword ptr ds:[0x12345678] //將進(jìn)程B 0x12345678的值存的eax中 mov dword ptr ds:[0x00401234],eax //將數(shù)據(jù)存儲到0x00401234中 mov cr3,A.DirectoryTableBase //切換回Cr3的值這段代碼的問題在于,當(dāng)我切換CR3為B進(jìn)程的頁目錄基址時,讀取的是B進(jìn)程的內(nèi)存,那么讀取的這段數(shù)據(jù)該如何傳遞給A進(jìn)程呢?不管將這段數(shù)據(jù)放在哪個位置,始終都是B進(jìn)程的內(nèi)存空間。
這里需要回顧一下進(jìn)程的地址空間管理,低2GB是每個進(jìn)程私有的,而高2GB的操作系統(tǒng)共享的。如果在B進(jìn)程將讀取的數(shù)據(jù)放到高2GB共享的內(nèi)核空間,然后在切回CR3的時候,從高2GB取數(shù)據(jù),就解決了這個問題
NtReadVirtualMemory流程解析
我們來看一下NtReadVirtualMemory是如何解決這個問題的:
NtWriteVirtualMemory和NtReadVirtualMemory的執(zhí)行流程類似
總結(jié)
以上是生活随笔為你收集整理的进程线程007 进程挂靠与跨进程读写内存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程线程006 Windows线程切换-
- 下一篇: APC机制详解