?? jiurl玩玩win2k進程線程篇 eprocess.htm
字號:
uint32 LowPart<BR>+264 int32 HighPart<BR>+260 int64 QuadPart<BR>+268
uint32 CommitChargeLimit<BR>+26c uint32 CommitChargePeak<BR>+270 struct
_LIST_ENTRY ThreadListHead<BR>+270 struct _LIST_ENTRY *Flink<BR>+274
struct _LIST_ENTRY *Blink<BR>+278 struct _RTL_BITMAP
*VadPhysicalPagesBitMap<BR>+27c uint32 VadPhysicalPages<BR>+280 uint32
AweLock<BR><BR><B>遍歷所有進程</B><BR><BR>系統需要能夠方便的遍歷所有進程。所以進程的 EPROCESS
結構使用鏈表鏈在了一起。<BR><BR>所有進程(除了Idle進程)的 EPROCESS 通過 EPROCESS 結構偏移+a0處的
LIST_ENTRY ActiveProcessLinks 鏈在一起。<BR>+0a0 struct _LIST_ENTRY
ActiveProcessLinks<BR>+0a0 struct _LIST_ENTRY *Flink<BR>+0a4 struct
_LIST_ENTRY *Blink<BR><BR>通過全局變量 PsActiveProcessHead 可以找到這個鏈。<BR>PID 為 0 的
Idle 進程并沒有鏈在這個鏈上。我們可以通過全局變量 PsIdleProcess 找到 Idle 進程的 EPROCESS。<BR><BR>使用
LIST_ENTRY 結構來組織鏈表在 Win2k 中非常常見。Flink,Blink指向的都是另一個 LIST_ENTRY
結構。<BR><BR>對于 Win2k Build 2195 來說,全局變量 PsActiveProcessHead 的地址是
0x8046a180,開始處就是一個 LIST_ENTRY 結構。順著這個 LIST_ENTRY 結構的 Flink 或者
Blink,我們可以找到每一個進程(除了Idle)的EPROCESS 結構中的 LIST_ENTRY ActiveProcessLinks。這個
ActiveProcessLinks 地址偏移 -0a0 處就是 EPROCESS 結構的地址。從 PsActiveProcessHead
開始順著這條鏈一直走下去,直到重新回到 PsActiveProcessHead
,鏈已經循環了,就說明已經遍歷了整個鏈。<BR><BR>下面我們看實際的例子<BR><BR>kd> ?
PsActiveProcessHead<BR>? PsActiveProcessHead<BR>Evaluate expression:
-2142854784 = 8046a180<BR>// Win2k Build 2195 的 PsActiveProcessHead 位于地址
8046a180<BR><BR>kd> dd 8046a180 l 2<BR>dd 8046a180 l 2<BR>8046a180
8141e0c0 82fa4b00<BR>// 8046a180 處的 LIST_ENTRY,注意 PsActiveProcessHead
并不是某個進程的 +0a0 ActiveProcessLinks<BR>// 我們看看 Flink 找到的進程<BR><BR>kd> ?
8141e0c0-a0<BR>? 8141e0c0-a0<BR>Evaluate expression: -2126389216 =
8141e020<BR>// ActiveProcessLinks 位于 EPROCESS 的 +0a0 處。<BR>// 我們得到的是
ActiveProcessLinks 的地址,所以 EPROCESS 在得到地址 -0a0 處。<BR><BR>kd> !process
8141e020 0<BR>!process 8141e020 0<BR>PROCESS 8141e020 SessionId: 0 Cid:
0008 Peb: 00000000 ParentCid: 0000<BR>DirBase: 00030000 ObjectTable:
81452a68 TableSize: 46.<BR>Image: System<BR>// 是PID為8的進程
System<BR>... <BR>// 順著 Flink 一直走下去,我們省去了中間的過程<BR><BR>kd> dd
82fa4b00 l 2<BR>dd 82fa4b00 l 2<BR>82fa4b00 8046a180 807e90c0<BR>// 看到了這個
ActiveProcessLinks 的 Flink 為 8046a180 ,就是 PsActiveProcessHead <BR>//
說明已經遍歷了整個 ActiveProcessLinks 鏈<BR><BR>對于 Win2k Build 2195 來說,全局變量
PsIdleProcess 的地址是 0x8046a1fc,是一個 EPROCESS 指針,直接指向 Idle 進程的 EPROCESS
結構。<BR><BR>kd> ? PsIdleProcess<BR>? PsIdleProcess<BR>Evaluate
expression: -2142854660 = 8046a1fc<BR>// Win2k Build 2195 的 PsIdleProcess
位于地址 8046a1fc<BR><BR>kd> dd 8046a1fc l 1<BR>dd 8046a1fc l 1<BR>8046a1fc
8046bb60<BR>// PsIdleProcess 的值為 8046bb60<BR><BR>kd> !process 8046bb60
0<BR>!process 8046bb60 0<BR>PROCESS 8046bb60 SessionId: 0 Cid: 0000 Peb:
00000000 ParentCid: 0000<BR>DirBase: 00030000 ObjectTable: 81452a68
TableSize: 46.<BR>Image: Idle<BR>// 可以看到 8046bb60 是指向 EPROCESS 的,并且這個
EPROCESS 是 PID 為 0 的 Idle 進程的
EPROCESS。<BR><BR><BR><B>進程名</B><BR><BR>EPROCESS 中保存著一個給人看的進程名,Windows
任務管理器中顯示的進程名,就是從這里獲得的。<BR>更準確的叫法是映像名稱。<BR>+1fc byte
ImageFileName[16]<BR>ImageFileName[16]
是一個16個字節長的字節數組,保存著進程名。當進程名的長度大于等于16個字節時,在 ImageFileName[16]
只保存進程名的前15個字節,ImageFileName[16]
最后一個字節為0,字符串的結束符。<BR>不同進程的進程名可以相同,比如打開多個記事本,那么每個記事本的 ImageFileName[16] 都是
"NOTEPAD.EXE",進程名只是給人看的,每個進程的 進程ID 都是不同的。<BR><BR>kd> !process 200
0<BR>!process 200 0<BR>Searching for Process with Cid == 200<BR>PROCESS
8105c2e0 SessionId: 0 Cid: 0200 Peb: 7ffdf000 ParentCid: 01f0<BR>DirBase:
00564000 ObjectTable: 81090c48 TableSize: 298.<BR>Image:
Explorer.exe<BR><BR>kd> db 8105c2e0+1fc l 10<BR>db 8105c2e0+1fc l
10<BR>8105c4dc 45 78 70 6c 6f 72 65 72-2e 65 78 65 00 00 00 00
Explorer.exe....<BR><BR><BR><B>進程ID (PID)</B><BR><BR>+09c void
*UniqueProcessId<BR><BR><B>父進程ID</B> <BR><BR>+1c8 void
*InheritedFromUniqueProcessId<BR><BR><BR><B>頁目錄</B><BR><BR>+018 uint32
DirectoryTableBase[2]<BR><BR>每個進程有自己4G地址空間,當進程切換時,就需要切換地址空間。也就是切換頁目錄頁表。所以每個進程都需要保存自己頁目錄的地址。每個進程的頁目錄所在物理頁的物理地址,就保存在進程
EPROCESS 結構偏移 +018 處 DirectoryTableBase數組的第0項中,即
DirectoryTableBase[0]。對于執行地址轉換的 CPU
來說需要知道頁目錄所在物理頁的物理地址就可以進行地址轉換。對于維護進程的頁目錄和頁表的系統來說,需要把頁目錄和頁表所在的物理頁映射到地址空間中。<BR><BR>下面使用
SoftICE 來說明,進程 EPROCESS 中的
DirectoryTableBase數組,有2個元素,其中DirectoryTableBase[0] 就是該進程的頁目錄的物理地址,也就是該進程
CR3 的值。<BR><BR>:addr<BR>CR3 LDT Base:Limit KPEB Addr PID Name<BR>00030000
8141E020 0008 System<BR>04E4B000 810F7580 008C smss<BR>06582000 810E8D60
00A8 csrss<BR>07607000 810CC2C0 00BC winlogon<BR>07A49000 810C1500 00D8
services<BR>0799A000 810BFD60 00E4 lsass<BR>00AFD000 810A2D60 0174
svchost<BR>00F21000 81092940 0190 svchost<BR>007C4000 8105D600 0200
Explorer<BR>024B9000 824D0020 0260 internat<BR>042B2000 8423E860 0180
conime<BR>*00030000 8046BB60 0000 Idle<BR>// addr 列出當前進程,注意每個進程的 CR3 ,
SoftICE 顯示的 KPEB Addr 就是 EPROCESS 的地址<BR><BR>// 轉換到 explorer
的地址空間,EPROCESS 在 8105D600 <BR>// 偏移 +018 處 是 uint32
DirectoryTableBase[2] 數組<BR>:addr explorer<BR>:dd 8105d600+18 l
10<BR>0010:8105D618 007C4000 06165000 00000000 00000000
.@|..P..........<BR>// DirectoryTableBase[0] 物理地址為 007C4000
看看這個物理地址,被映射到了哪些虛擬地址<BR>:phys 7c4000<BR>807C4000<BR>C0300000<BR>//
C0300000 是進程頁目錄被映射到的地址空間<BR>:phys
6165000<BR>86165000<BR>C0301000<BR><BR>// 情況一樣<BR>:addr internat<BR>:dd
824d0020+18 l 10<BR>0010:824D0038 024B9000 024BA000 00000000 00000000
..K...K.........<BR>:phys 24b9000<BR>824B9000<BR>C0300000<BR>:phys
24ba000<BR>824BA000<BR>C0301000<BR><BR>// 情況一樣<BR>:addr csrss<BR>:dd
810e8d60+18 l 10<BR>0010:810E8D78 06582000 06563000 00000000 00000000 .
X..0V.........<BR>:phys 6582000<BR>86582000<BR>C0300000<BR>:phys
6563000<BR>86563000<BR>C0301000<BR><BR>// 注意這是 System 進程<BR>// System 的
DirectoryTableBase[0] 仍然映射到了 C0300000<BR>// 但是 System 的
DirectoryTableBase[1] 情況和別的進程不一樣<BR>:addr system<BR>:dd 8141e020+18 l
10<BR>0010:8141E038 00030000 00000000 00000000 00000000
................<BR>:phys
30000<BR>80030000<BR>C0300000<BR>C04FB000<BR>EB3F1000<BR>F09CA000<BR>:phys
00000000<BR>80000000<BR>C0200000<BR>EB3C1000<BR>F09D4000<BR><BR>我們可以看到
DirectoryTableBase[0] 中就是頁目錄所在物理頁的物理地址。每個進程 EPROCESS +18 處4個字節的值 和 該進程 CR3
中的值是一樣的。 EPROCESS +18 處4個字節的物理地址都映射到了 C0300000,而 C0300000
正是頁目錄所在物理頁映射到地址空間中的虛擬地址。我也檢查了一個進程的 DirectoryTableBase[0] (EPROCESS +18
處4個字節的值)所指的物理頁中的內容,里面的確是 該進程頁目錄的內容。<BR><BR>從 SoftICE 的輸出中,我們可以看到
DirectoryTableBase[1] 中的物理地址所指的物理頁,被映射到了地址空間 C0301000 處,也就是
C0301000-C0301FFF 這個頁表,我們計算可以得到這個頁表負責的是 C0400000-C07fffff 這 4M
虛擬地址空間的映射。我們知道每個進程的 Working Set 開始于 C0502000 ,就落在這個范圍之內,而每個進程都有自己的 Working
Set。<BR><BR>其實一個進程保存自己的頁目錄的物理地址就足夠了。由于每個進程的 Working Set 在
C0400000-C07fffff 這個范圍之內,每個進程的 Working Set
是不同的,所以負責這個范圍的頁表也是不同的,所以有理由多保存一個負責地址空間 映射的頁表的物理地址。不過要注意的是,進程 System 的
DirectoryTableBase[1] 值為0,并不是對應地址空間范圍 C0400000-C07fffff
的頁表所在物理頁的物理地址。<BR><BR><BR><B>不切換進程,直接訪問其他進程的地址空間</B><BR><BR>我們使用(驅動程序中使用)KeAttachProcess(),KeDetachProcess()
切換地址空間到一個指定進程的地址空間,然后訪問這個進程地址空間中的內存。現在對于物理內存不超過 512M
的系統,我們有一種方法,可以不用切換地址空間,直接訪問指定進程的地址空間中的內存。<BR><BR>我們知道 Win2k 把物理內存的前 512M
的每個物理頁,一一對應的映射到地址空間的 80000000-9FFFFFFF 這一段 LargePage
區域中。物理內存不到512M的,有多少就映射多少。這樣我們就可以訪問所有不超過 512M 的物理內存(當然 80000000-9FFFFFFF
在系統地址空間中,訪問需要 ring0 的權限)。對于一個小于512M的物理地址,我們可以用這個物理地址加上 80000000
,得到該物理頁映射的一頁地址空間的虛擬地址,我們可以使用這個虛擬地址,來訪問這個物理中的內容。從前面分析 DirectoryTableBase
時,SoftICE 的輸出也可以看出這一點,比如,物理地址 7c4000 ,對應虛擬地址 807C4000
。<BR><BR>對于一個指定的進程,我們可以通過進程鏈表找到它,并計算出它的 EPROCESS 的地址。就可以找到 EPROCESS
中的該進程頁目錄所在物理頁的物理地址,即 DirectoryTableBase[0]
的值。該進程頁目錄所在物理頁的物理地址如果小于512M,我們就可以計算出這個物理頁被映射到地址空間 80000000-9FFFFFFF
中的虛擬地址,就可以訪問該進程的頁目錄。就可以找到該進程的所有頁表的物理地址,同樣我們根據頁表的物理地址,可以算出頁表在
80000000-9FFFFFFF
中的虛擬地址,就可以訪問該進程的所有頁表。就可以找到該進程所有頁對應的物理頁的物理地址。根據這個物理地址,我們可以算出該物理頁在
80000000-9FFFFFFF 中的虛擬地址,從而訪問改進程的某一頁中的數據。<BR><BR>我們用 SoftICE 實際演示一下,讀取進程
Explorer 地址空間 0x400000 中的內容。<BR><BR>:addr<BR>CR3 LDT Base:Limit KPEB Addr
PID Name<BR>00030000 8141E020 0008 System<BR>04E4B000 810F7580 008C
smss<BR>06582000 810E8D60 00A8 csrss<BR>07607000 810CC2C0 00BC
winlogon<BR>07A49000 810C1500 00D8 services<BR>0799A000 810BFD60 00E4
lsass<BR>00AFD000 810A2D60 0174 svchost<BR>00F21000 81092940 0190
svchost<BR>007C4000 8105D600 0200 Explorer<BR>024B9000 824D0020 0260
internat<BR>06A45000 84090220 02A8 conime<BR>*00030000 8046BB60 0000
Idle<BR>// Explorer 的 EPROCESS 地址為 8105D600 ,我們通過進程鏈表也可以找到<BR><BR>:dd
8105d600+18 l 10<BR>0010:8105D618 007C4000 06165000 00000000 00000000
.@|..P..........<BR>// Explorer 的頁目錄所在物理頁的地址為 007C4000 <BR><BR>:dd
(80000000+7c4000)+(400000>22)*4 l 10<BR>0010:807C4004 028D4067 0268E067
01E35067 0219B067 g@..g.h.gP..g...<BR>// (80000000+7c4000) 就是 Explorer
的頁目錄的虛擬地址<BR>// (400000>22) 取虛擬地址 400000 的高10bit,這是頁目錄索引。<BR>//
一個頁目錄項大小為4個字節,所以 (400000>22)*4 為 400000 對應頁目錄項在頁目錄中偏移地址<BR>// 所以 400000
對應的頁表的物理地址為 028D4000 (別忘了頁目錄項和頁表項的低12位是標志)<BR><BR>:dd
(80000000+28d4000)+((400000&3ff000)>12)*4 l 10<BR>0010:828D4000
02963005 00612025 08332C34 02954025 .0..% a.4,3.%@..<BR>//
((400000&3ff000)>12) ,取虛擬地址 400000 作為頁表索引的10bit,<BR>//
((400000&3ff000)>12)*4 為 400000 在對應頁表項在頁表中偏移地址<BR>// 所以 400000
對應的物理頁的物理地址為 02963000<BR><BR>:dd (80000000+2963000)+(400000&fff) l
10<BR>0010:82963000 00905A4D 00000003 00000004 0000FFFF
MZ..............<BR>// (400000&fff),取虛擬地址 400000 的低12bit作為頁內偏移。<BR>//
最后得到進程 Explorer 虛擬地址 400000 處的4個字節值為 00905A4D ,就是"MZ.."<BR><BR>// 切換到
Explorer 地址空間,驗證一下<BR>:addr explorer<BR>:dd 400000 l 10<BR>0010:00400000
00905A4D 00000003 00000004 0000FFFF MZ..............<BR>// 可以看到 Explorer
地址空間虛擬地址 00400000 處的值正是 00905A4D ,"MZ.."<BR><BR><BR><B>進程的
HANDLE_TABLE</B><BR><BR>+128 struct _HANDLE_TABLE
*ObjectTable<BR><BR><B>進程的 PEB</B><BR><BR>+1b0 struct _PEB
*Peb<BR><BR><B>線程鏈表</B><BR><BR>進程的所有線程通過 LIST_ENTRY
結構鏈在了一個雙向循環鏈表上。<BR>一個鏈表是以 EPROCESS 結構的 KPROCESS Pcb 中的 ThreadListHead
為鏈表的鏈表頭。鏈上的每一項是一個線程的 KTHREAD ETHREAD 結構的 Tcb 中的 ThreadListEntry
。<BR>另一個鏈表是以 EPROCESS 結構中的 ThreadListHead 為鏈表的鏈表頭。鏈上的每一項是一個線程的 ETHREAD
結構中的 ThreadListEntry 。<BR>通過這兩個鏈表中的任何一個,都可以找到一個進程的所有線程的 ETHREAD 結構,當然找到
ETHREAD 結構,就可以找到 ETHREAD 結構中的 KTHREAD。<BR><BR>KTHREAD 鏈表<BR><BR>struct
_EPROCESS (sizeof=648)<BR>+000 struct _KPROCESS Pcb<BR>+050 struct
_LIST_ENTRY ThreadListHead<BR>+050 struct _LIST_ENTRY *Flink<BR>+054
struct _LIST_ENTRY *Blink<BR><BR>struct _ETHREAD (sizeof=584)<BR>+000
struct _KTHREAD Tcb<BR>+1a4 struct _LIST_ENTRY ThreadListEntry<BR>+1a4
struct _LIST_ENTRY *Flink<BR>+1a8 struct _LIST_ENTRY *Blink<BR><BR>ETHREAD
鏈表<BR><BR>struct _EPROCESS (sizeof=648)<BR>+270 struct _LIST_ENTRY
ThreadListHead<BR>+270 struct _LIST_ENTRY *Flink<BR>+274 struct
_LIST_ENTRY *Blink<BR><BR>struct _ETHREAD (sizeof=584)<BR>+240 struct
_LIST_ENTRY ThreadListEntry<BR>+240 struct _LIST_ENTRY *Flink<BR>+244
struct _LIST_ENTRY *Blink<BR><BR><B>VAD </B><BR><BR>+194 void
*VadRoot<BR>+198 void *VadHint<BR><BR>VadRoot 是進程 Vad
二叉樹的根節點的指針。<BR>通過反匯編和分析函數 MiLocateAddress() 可以知道 ,VadHint 是上一次
MiLocateAddress() 找到的 Vad 的指針。<BR><BR><B>WorkingSet</B><BR><BR>+0e0 uint32
PeakWorkingSetSize<BR>+0e4 uint32 WorkingSetSize<BR>+0e8 uint32
MinimumWorkingSetSize<BR>+0ec uint32 MaximumWorkingSetSize<BR>+0f0
*VmWorkingSetList<BR><BR>WorkingSet 的相關信息。<BR>VmWorkingSetList 是指向 MMWSL
結構的指針。值總是 C0502000 。<BR><BR><B>Section</B><BR><BR>+1ac void
*SectionHandle<BR>+1b4 void *SectionBaseAddress<BR><BR>SectionHandle
是句柄,可以用這個句柄在句柄表中找到對應的對象。<BR>SectionBaseAddress
是載入地址空間的基地址。<BR><BR><BR><B>進程對象的對象體</B><BR><BR>需要說明的是 EPROCESS
就是進程對象的對象體,象其他類型的對象一樣,EPROCESS 之前也有對象頭。使用 kd 可以很容易看到這一點<BR>kd> !process
8 0<BR>!process 8 0<BR>Searching for Process with Cid == 8<BR>PROCESS
8141e020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000<BR>DirBase:
00030000 ObjectTable: 81452a68 TableSize: 106.<BR>Image:
System<BR><BR>kd> !object 8141e020<BR>!object 8141e020<BR>Object:
8141e020 Type: (814524e0) Process<BR>ObjectHeader:
8141e008<BR>HandleCount: 2 PointerCount: 36<BR><BR>根據 EPROCESS 的地址,kd
可以正確分析出對象類型是進程,說明了 EPROCESS 的確是對象體。<BR><BR><BR>歡迎交流,歡迎交朋友,<BR>歡迎訪問 <A
href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A> <A
href="http://jiurl.cosoft.org.cn/forum">http://jiurl.cosoft.org.cn/forum</A>
<BR><BR><BR><BR><BR><BR><BR><BR></P></TD></TR></TBODY></TABLE></DIV></BODY></HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -