内核对象和句柄

时间:2021-11-04 17:36:25
内核对象的数据结构只能由操作系统的内核访问,所以应用程序不能再内存中定位这些数据结构并直接修改其内容。
可以利用windows的api函数进行操作。当调用一个会创建内核对象的函数后,会返回一个句柄,它标识了所创建的对象。
这些句柄值是与进程相关的,如果传递给其他进程,可能会失败,因为每个进程会有一个句柄表,所以使用的时候可能是完全不用的内核对象。
内核对象的所有者是操作系统,当进程创建一个内核对象,然后进程终止,则内核对象并不一定销毁。
大多数情况下,这内核对象是会销毁的,但是假如另外一个进程也在使用我们进程创建的内核对象,那么在其他进程使用完他之前,他是不会销毁的。
操作系统内核知道有多少进程正在使用一个特定的内核对象,因为每个对象都包含了一个使用计数!,只有当使用计数为0,操作系统才会销毁这内核对象。
用于插件内核对象的所有函数基本都有一个指向SECURITY_ATTRIBUTES结构的指针作为参数,一般都是传入NULL,这样创建的内核对象具有默认的安全性,要取决于当前进程的安全令牌。
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; / /结构体的大小,可用SIZEOF取得
LPVOID lpSecurityDescriptor; / /安全描述符
BOOL bInheritHandle ;/ /安全描述的对象能否被新创建的进程继承
} SECURITY_ATTRIBUTES,* PSECURITY_ATTRIBUTES;
一个进程在初始化时,系统将会为他分配一个句柄表,这句柄表供内核对象使用,不适用于用户对象和GDI对象。
用于插件内核对象的函数都会返回一个与进程相关的句柄,系统用所以来表示内核对象的信息保存在进程句柄表中的具体位置。
无论以什么方式创建内核对象,我们都需要调用closehandle来关闭内核对象。这时候会通过句柄值找到内核对象的数据结构的地址,减少结构中的使用计数,如果使用计数为0,则销毁内核对象。
跨进程边界共享内核对象 三种方法
使用对象句柄继承:只有在一个父子关系的时候才可以使用,当父进程创建一个内核对象,父进程必须向系统指出它希望这对象的句柄是可以继承的。
为了插件一个可继承的句柄,父进程必须分配并初始化一个SECURITY_ATTRIBUTES,将bInheritHandle设置为true,这时候创建出来的对象在句柄表中的标志就是可继承的。
下一步是父进程生成子进程,
BOOL CreateProcess
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATIONlpProcessInformation
);
其中的bInheritHandles设置为true,这样子进程就会继承父进程的可继承的句柄的值。系统会遍历父进程的句柄表,对他的每个记录项进行检查,凡是包含一个有效的可继承的标识,,都会被完整的复制到子进程的句柄表。除了复制句柄表的记录项,系统还会递增内核对象的使用计数。对于32bit系统,内核地址在0x80000000到0xffffffff,对于64bit的系统则是0x000004000 00000000 到0xffffffff ffffffff。
改变句柄的标志
BOOL WINAPI SetHandleInformation(
_In_ HANDLE hObject,
_In_ DWORD dwMask,
_In_ DWORD dwFlags
);
第一个参数hObject标识了一个有效句柄。
第二个参数dwMask告诉函数我们想更改哪个或者哪些标志:
1\ HANDLE_FLAG_INHERIT 用CreateProcess(bInheritHandle设为TRUE)创建出来的子进程可以继承对象句柄
2\HANDLE_FLAG_PROTECT_FROM_CLOSE 无法调用CloseHandle关闭对象句柄
第三个参数dwFlags指出希望把标志设为什么。
跨进程共享内核对象的第二个方法就是为对象命名,不过不怎么推介。
第三种就是复制对象句柄,这应该是比较常见的。
BOOL WINAPI DuplicateHandle(
__in HANDLE hSourceProcessHandle,
__in HANDLE hSourceHandle,
__in HANDLE hTargetProcessHandle,
__out LPHANDLE lpTargetHandle,
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in DWORD dwOptions
);
hSourceProcessHandle:源进程内核句柄(即负责传递 内核对象句柄的进程句柄)
hSourceHandle:要传递的内核对象句柄
hTargetProcessHandle:目标进程内核句柄
lpTargetHandle:接收内核对象句柄的地址
dwDesiredAccess:TargetHandle句柄使用何种访问掩码(这个掩码是在 句柄表中的一项)
bInheritHandle:是否拥有继承
dwOptions:当设DUPLICATE_SAME_ACCESS时,表示目标句柄拥有和源进程的句柄一样的访问掩码,如果设置次参数,会忽略dwDesiredAccess。
                    当设DUPLICATE_CLOSE_SOURCE时,传输完后,关闭源中的 内核对象句柄。
//ALL of the following code is executed by Process S.
//Createamutex object accessible by Process S.
HANDLE hObjProcessS = CreateMutex(NULL, FALSE, NULL);

//Open ahandle to Process T's kernel object.
HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, dwProcessIdT);

//Anuninitilized handle relative to Process T.
HANDLE hObjProcessT;

//GiveProcess T accesss to our mutex object
DuplicateHandle(GetCurrentProcess(),
hObjProcessS,
hProcessT,
&hObjProcessT, 0, FALSE,
DUPLICATE_SAME_ACCESS);

//Usesome IPC mechanism to get the handle
//valuein hOnjProcess S into Process T
//We nolonger need to communicate with Process T.
CloseHandle(hProcessT);

//WhenProcess S no longer needs to Use the mutex,
//itshould close it.
CloseHandle(hObjProcessS);