第三章:内核对象

来源:互联网 发布:php 获取上传图片类型 编辑:程序博客网 时间:2024/05/16 09:36

1:内核对象只是一个内存块,这个内存块是一个数据结构,只能由操作系统访问,程序通过调用API控制他,创建一个内核对象会返回一个Handle,在32位系统下是32位的一个值,在64位系统下是64位的一个值,Handle是进程相关的

2:内核对象所有者是操作系统,所以其生命周期可能会比进程长,内核对象数据结构中有个使用计数,创建是被初始化为1,当变成0后,内核对象被操作系统删除

3:每个内核对象都有一个安全描述符,创建时一般传入NULL,NULL为默认安全性,这取决于当前进程安全令牌(security token).如果用Openxxx函数打开一个内核对象,会进行一次安全检查,与创建时制定的安全描述符比对,内核对象和GDI对象的区别就是有没有安全描述符

4:进程创建时,会创建一个句柄表,表的每一行包括:索引,指向内核对象内存快指针,访问掩码,标志

5:一个进程首次初始化后,句柄表为空,如果创建了一个内核对象,会分配一块内存,根据安全描述符分配访问掩码,根据当前句柄是否能继承设置标志,句柄值右移4位就是句柄表的索引,但这可能在以后windows版本中发生变化,后4位右windows维护,所以目前最小的句柄值是4

6:CloseHandle()关闭内核对象,操作系统会将引用计数-1,当计数为0后,操作系统删除次内核对象,进程销毁的时候,操作系统会检查句柄表,帮助进程关闭所有忘记关闭的内核对象,与此相似,进程结束后,不会泄露任何句柄,内存,GDI对象,可以使用Process Explorer查看句柄相关信息

7:为何要把内核对象设计成进程相关而不是操作系统相关,是因为如果是后者,一个进程会很容易破坏另外一个进程内核对象

8:跨进程共享内核变量

8.1:使用对象句柄继承

注意这是句柄继承,不是内核对象继承,创建内核对象时,安全描述符如下

SECURITY_ATTRIBUTES sa;sa.nLength=sizeof(sa);sa.lpSecurityDescriptor=NULL;sa.bInheritHandle=true;

安全描述符bInheritHandle为true则句柄表标志位将被值1,一帮情况下为0

然后CreateProcess参数中设置bInheritHandle=true;

这样创建进程后,子进程会不忙执行自己的代码,而是遍历父进程句柄表,如果句柄值有效,则复制到自己句柄表相应的位置,注意这里位置是一一对应的,复制会导致此内核对象使用计数+1

对象句柄继承只发生在CreateProcess()的一瞬间,如果子进程创建完成,父进程又创建新的句柄,则这些句柄不会被继承

需要通过进程共享手段告诉子进程继承了那些句柄

方法1:将句柄值作为命令行参数传递给子进程

方法2:使用进程通信计数

方法3:让子进程继承父进程的环境变量

8.2:改变句柄的标志

调用函数SetHandleInformation()改变句柄标志

//打开继承标志SetHandleInformation(hd,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);//关闭继承标志SetHandleInformation(hd,HANDLE_FLAG_INHERIT,0);

检查一个句柄是否可以继承

DROWD dwFlags;GetHandleInformation(hobj,&dwFlags);bool IsInheritable=(0!=dwFlags&HANDLE_FLAG_INHERIT));

8.3:为内核对象命名

用Createxxx函数创建内核对象并加上名字,系统会首先检查是否有同名对象存在,如果是,检查类型是否相同,不同则返回,否则系统执行一次安全检查,验证调用者是否拥有对该对象的完全访问权限,如果是,系统会在调用者进程句柄表中找到一个空白项并赋值,注意这个句柄值可能和创建者句柄值不同

Openxxx同样能打开一个内核对象,但如果不存在,不会自动创建,Openxxx和Createxxx打开既有内核对象,会忽略打开时使用的安全描述符和继承属性

用于创建内核对象的函数,总是返回具有完全访问权限的句柄,如果想限制一个句柄的访问权限,可以使用ex扩展版本
8.4:命名空间

1:获得自己当前会话ID

DWORD processID=GetCurrentProcessID();DWORD sessionID;ProecssIdToSessionId(processID,&sessionID);

2:可以通过创建专有命名空间防止名称冲突

8.5:复制对象句柄

DuplicateHandle();此函数将一个进程句柄复制到另外一个进程,复制后句柄值不一定相同,dwOptions可以指定DUPLICATE_SAME_ACCESS,表示复制和原进程同样的访问掩码

1:以下代码演示原进程复制一个句柄到目的进程

HANDLE hd=CreateMutex(NULL,FALSE,NULL);HANDLE hdProcessT=OpenProcess(PROCESS_ALL_ACCESS,NULL,dwProcessID);HANDLE hdDup;DuplicateHandle(GetCurrentProcess(),hd,hdProcessT,&hdDup,0,FALSE,DUPLICATE_SAME_ACCESS);//用进程通信手段将此句柄hdDup传给hdProcessTCloseHandle(hd);CloseHandle(hdProcessT);

2:以下代码展示使用DupcliteHandle的另一个目的:创建一个句柄后,将此句柄传递给一个函数,但是想给他不同的访问权限

如下,创建时时读写权限,但只想给调用函数只读权限

HANDLE hFileMapView=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,10240,NULL);HANDLE hDup;DuplicateHandle(GetCurrentProcess(),hFileMapView,GetCurrentProcess(),&hDup,FILE_MAP_READ,FALSE,0);//使用这个hDupCloseHandle(hFileMapView);CloseHandle(hDup);

 

最后:一个句柄通过命令行继承的例子
B进程等A进程退出后再打印文字

//进程A#include "stdafx.h"#include <Windows.h>int _tmain(int argc, _TCHAR* argv[]){STARTUPINFO si={sizeof(si)};PROCESS_INFORMATION pi;ZeroMemory(&pi,sizeof(pi));SECURITY_ATTRIBUTES sa;  sa.nLength=sizeof(sa);  sa.lpSecurityDescriptor=NULL;  sa.bInheritHandle=true; TCHAR szBuf[]=_T("TestCall.exe");HANDLE hdProcessT=OpenProcess(PROCESS_ALL_ACCESS,true,GetCurrentProcessId());  TCHAR szHandle[100]={0};_stprintf(szHandle,_T("%d"),(int)hdProcessT);//这里不ClosheHandle(hdProcessT);bool ret=CreateProcess(szBuf,szHandle,&sa,NULL,true,NULL,NULL,NULL,&si,&pi);CloseHandle(pi.hProcess);CloseHandle(pi.hThread);CloseHandle(hdProcessT);//这里Closesystem("pause");return 0;}
//进程B#include "stdafx.h"#include <Windows.h>#include <vector>#include <algorithm>using namespace std;int _tmain(int argc, _TCHAR* argv[]){LPSTR p=GetCommandLine();HANDLE hd=(HANDLE)(atoi(p));WaitForSingleObject(hd,INFINITE);Sleep(1);cout<<"OVER"<<endl;return 0;}


 

原创粉丝点击