进程与线程的关系:
一个逃程可以包含多个线程
一个进程至少要有一个线程
进程为线程提供资源,也就是提供Cr3的值, Cr3中存储的是页目录表基址, Cr3确定了,线程能访问的内存也就确定了。
进程与线程的关系
线程代码:
mov eax, dword ptr ds:(0x12345678]
CPU如何解析0x12345678这个地址呢?
- CPU解析线性地址时要通过页目录表来找对应的物理页,页目录表基址存在 Cr3寄存器中。
- 当前的Cr3的值来源于当前的进程(KPROCESS.DirectoryTableBase(+0x018))。
线程与进程如何关联
ETHREAD结构体:
...
+0x034 ApcStateI
...
+0x000 ApcListHead
+0x010 Process
+0x014 KernelApclnProgress
+0x015 KernelApcPending
+0x016 UserApcPending
...
+0x220 ThreadsProcess//指向此线程所属的进程
...
KTHREAD:+0x34 ApcState
kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS//指向此线程所属的进程
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
养父母负责提供Cr3
线程切换的时候,会比较_KTHREAD结构体0x044处指定的EPROCESS是否为同一个,如果不是同一个,会将0x044处指定的EPROCESS的DirectoryTableBase的值取出,赋值给Cr3。
所以,线程需要的Cr3的值来源于0x044处偏移指定的EPROCESS。
总结:
0x220亲生父母:这个线程谁创建的
0x044养父母:谁在为这个线程提供资源(也就是提供Cr3)
一般情况下, 0x220与0x44指向的是同一个进程
.4, Cr3的值可以随便改吗
正常情况下, Cr3的值是由养父母提供的,但Cr3的值也可以改成和当前线程豪,不相干的其他进程的DirectoryTableBase
线程代码:
mov cr3,A.DirectoryTableBase
mov eax,dword ptr ds:[0x12345678] //A进程的0x12345678内存
mov cr3,B.DirectoryTableBase
mov eax,dword ptr ds:[0x12345678] //B进程的0x12345678内存
mov cr3,C.DirectoryTableBase
mov eax,dword ptr ds:[0x12345678] //C进程的0x12345678内存
将当前CR3的值改为其他进程,称为进程挂靠
分析NtReadVirtualMemory函数
核心代码就在这函数中
真正COPY的函数
可不可以只修改Cr3而不修改养父母?不可以,如果不修改养父母的值,一旦产生线程切换,就会变成自己读自己!
如果我们自己来写这个代码,在切换Cr3后关闭中断,并且不调用会导致线程切换的api,就可以不用修改养父母的值。
总结:
正常情况下,当前线程使用的Cr3是由其所属进程提供的(ETHREAD 0x44偏移处指定的EPROCESS),正是因为如此, A进程中的线程只能访问A的内存。
如果要让A进程中的线程能够访问B进程的内存,就必须要修改Cr3的值为B进程的页目录表基址(B.DirectoryTableBase),这就是所谓的 进程挂靠.