进程与线程
一个进程可以包含多个线程
一个进程至少要有一个线程
进程为线程提供资源,也就是提供Cr3值,Cr3中存储的
是页目录表基址,Cr3确定了,线程能访问的内存也就确定了。
线程代码:
mov eax,dword ptr ds:[0x12345678]
CPU解析过程:
(1)CPU解析线性地址时要通过页目录表在找对应的物理页,页目录表基址存在
Cr3寄存器中。
(2)当前的Cr3的值来源于当前的进程(_KPROCESS.DirectoryTableBase(+0x018))
线程与进程的如何关联
ETHREAD结构体:
+0x034 ApcState
+0x000 ApcListHead
+0x010 Process *
+0x014 KernelApclnProgress
+0x015 KernelApcPending
+0x016 UserApcPending
+0x220 ThreadsProcess *(线程所属进程)
Process与ThreadProcess的指向区别
线程切换的时候,会比较_KTHREAD结构体0x044处指定的EPROCESS是否为同一个,
如果不是同一个,会将Process指定的EPROCESS的DirectoryTableBase的值取出来,
赋值给Cr3。
所以,线程需要的Cr3的值来源于0x044(Process)处偏移指定的EPROCESS。
总结:
0x220 ThreadsProcess:这个线程谁创建的。
0x44=0x034 ApcState+0x010 Process:谁在为这个线程提供资源(也就是提供Cr3)
一般情况下,0x220与0x44指向的是同一个进程。
进程挂靠
正常情况下,Cr3的值时由Process提供的,但是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的值改为其他进程,称为进程挂靠。
为什么要是用进程挂靠
因为当前线程的Cr3物理页能访问的范围都在当前范围,
如果使用了进程挂靠到其他Cr3的进程地址那么,物理页
范围将会是其他进程的范围。
分析NtReadVirtualMemory函数
NtReadVirtualMemeory->KiAttachProcess->修改Process->修改Cr3
需要注意的是修改Cr3也需要修改Process、如果不修改一旦线程切换,就会变成自己读自己(切换回来了)。
如果我们自己来写这个代码,在切换Cr3后关闭中断,并且不挑用会导致线程切换API,就可以不用修改
Process的值。
总结:
正常情况下,当前线程使用Cr3是由所属进程提供的(ETHREAD 0x44偏移处指定EPROCESS),
正因如此,A进程中的线程只能访问A的内容。
如果要让A进程中的线程能够访问B进程的内存,就必须要修改Cr3的值为B进程的页目录表基址
(B.DirectoryTableBase),这就是所谓的进程挂靠。