跨进程读写内存
跨进程的本质
跨进程的本质是进程挂靠正常情况下,A的进程的线程只能访问A进程
的地址空间,如果A进程的线程想要访问B进程的地址空间,就要修改当前
Cr3的值为B进程页目录表基值(KPROCESS.DirectoryTableBase).
即:mov cr3,B.DirectoryTableBase
NtReadVirtualMemory API 流程解析:
1.切换Cr3
2.将数据读复制到高2G
3.切换Cr3
4.从高2G复制到目标位置
该API功能是读取进程内存,原理是将目标进程数据移动
存放到高2G的暂存区中,之后在复制到当前进程中。
NtWriteVirtualMemory API 分析
1.将数据从目标位置复制到高2G地址
2.切换Cr3
3.从高2G复制到目标位置
4.切换Cr3
能够看出本质上和NtReadVirtualMmeory类似,
首先从当前进程读取数据存放到高2G的暂存区中,
因为所有进程的高2G物理页都是一致的因此都是可以读取的,
之后目标进程再从高2G的暂存区中读到目标位置。
句柄表
什么是句柄
当一个进程创建或者打开一个内核对象时,将获得一个句柄,
通过这个句柄可以访问内核对象。
内核对象本质上就是一种结构体,只有Ring0环才能进行读写的。
当然也有一些类似于笔刷这种用户层的句柄。
例如:
HANDLE g_hEvent=::CreateEvent(NULL,TURE,FALSE,NULL);
为什么要有句柄
句柄存在的目的就是为了避免在应用层直接修改内核对象。
HANDLE g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
如果g_hEvent存储的就是EVENT内核对象的地址,那么久意味着我们可以在应用
层修改这个地址,一旦指向了无效的内核内存地址就会蓝屏。
句柄表
Windows为了防止出现蓝屏的问题,设计了一个句柄表位于当前的
EPROCESS下,句柄实际上就是句柄表的索引,并且因此内核对象指针。
句柄表里存储了你打开或者创建的句柄,创建的句柄会新增到句柄表,
而打开的句柄会直接返回索引。
句柄表位于EPROCESS+0x0c4 ObjectTable :_HANDLE_TABLE
_HANDLE_TABLE
+0x000 TableCode 句柄表
+0x004 QuotaProcess
+0x008 UniqueProcess
+0x00c HandleTableLock
+0x01c HandleTableList
句柄计数器,比如连续打开100次内核对象即获得内核对象句柄,
会存在当前进程的句柄表里,而且只是创建了一个内核对象,打开了100次
只是将计数器累加,每次加一。
反调试:可以通过遍历其他进程内是否有打开自己的句柄判断自身是否被反调试。
总结:
1.一个进程可以创建、打开很多内核对象,这些内核对象的地址存储在当前进程的句柄表中。
我们在应用层得到的句柄值,实际上就是当前进程句柄表的索引。
2.同一个内核对象可以被不同的进程所引用,但句柄的值可能一样也可能不一样。
全局句柄表
1.所有的进程和线程无论是否打开,都在这个表中。
2.每个进程和线程都有一个唯一的编号:PIC和CID,这两个值
其实就是全局句柄表中的索引。
进程和线程的查询,主要是以下三个函数,按照给定的PID
或CID从PspCidTable(全局变量)从查找相应的进程和线程对象:
PsLookupProcessThreadByCid()
PsLookupProcessByProcessld()
PsLookupThreadByThreadld()
|