⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jiurl玩玩win2k进程线程篇 eprocess.htm

📁 关于win2000核心编程的文章
💻 HTM
📖 第 1 页 / 共 2 页
字号:
      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&gt; ? 
      PsActiveProcessHead<BR>? PsActiveProcessHead<BR>Evaluate expression: 
      -2142854784 = 8046a180<BR>// Win2k Build 2195 的 PsActiveProcessHead 位于地址 
      8046a180<BR><BR>kd&gt; 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&gt; ? 
      8141e0c0-a0<BR>? 8141e0c0-a0<BR>Evaluate expression: -2126389216 = 
      8141e020<BR>// ActiveProcessLinks 位于 EPROCESS 的 +0a0 处。<BR>// 我们得到的是 
      ActiveProcessLinks 的地址,所以 EPROCESS 在得到地址 -0a0 处。<BR><BR>kd&gt; !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>...&nbsp;<BR>// 顺着 Flink 一直走下去,我们省去了中间的过程<BR><BR>kd&gt; dd 
      82fa4b00 l 2<BR>dd 82fa4b00 l 2<BR>82fa4b00 8046a180 807e90c0<BR>// 看到了这个 
      ActiveProcessLinks 的 Flink 为 8046a180 ,就是 PsActiveProcessHead&nbsp;<BR>// 
      说明已经遍历了整个 ActiveProcessLinks 链<BR><BR>对于 Win2k Build 2195 来说,全局变量 
      PsIdleProcess 的地址是 0x8046a1fc,是一个 EPROCESS 指针,直接指向 Idle 进程的 EPROCESS 
      结构。<BR><BR>kd&gt; ? PsIdleProcess<BR>? PsIdleProcess<BR>Evaluate 
      expression: -2142854660 = 8046a1fc<BR>// Win2k Build 2195 的 PsIdleProcess 
      位于地址 8046a1fc<BR><BR>kd&gt; dd 8046a1fc l 1<BR>dd 8046a1fc l 1<BR>8046a1fc 
      8046bb60<BR>// PsIdleProcess 的值为 8046bb60<BR><BR>kd&gt; !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&gt; !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&gt; 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>&nbsp;<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&nbsp;<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&nbsp;<BR><BR>:dd 
      (80000000+7c4000)+(400000&gt;22)*4 l 10<BR>0010:807C4004 028D4067 0268E067 
      01E35067 0219B067 g@..g.h.gP..g...<BR>// (80000000+7c4000) 就是 Explorer 
      的页目录的虚拟地址<BR>// (400000&gt;22) 取虚拟地址 400000 的高10bit,这是页目录索引。<BR>// 
      一个页目录项大小为4个字节,所以 (400000&gt;22)*4 为 400000 对应页目录项在页目录中偏移地址<BR>// 所以 400000 
      对应的页表的物理地址为 028D4000 (别忘了页目录项和页表项的低12位是标志)<BR><BR>:dd 
      (80000000+28d4000)+((400000&amp;3ff000)&gt;12)*4 l 10<BR>0010:828D4000 
      02963005 00612025 08332C34 02954025 .0..% a.4,3.%@..<BR>// 
      ((400000&amp;3ff000)&gt;12) ,取虚拟地址 400000 作为页表索引的10bit,<BR>// 
      ((400000&amp;3ff000)&gt;12)*4 为 400000 在对应页表项在页表中偏移地址<BR>// 所以 400000 
      对应的物理页的物理地址为 02963000<BR><BR>:dd (80000000+2963000)+(400000&amp;fff) l 
      10<BR>0010:82963000 00905A4D 00000003 00000004 0000FFFF 
      MZ..............<BR>// (400000&amp;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&nbsp;</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&gt; !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&gt; !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 + -