1. 一个进程在初始化时,系统将会他分配一个空的句柄表,这个句柄表仅供内核对象使用,不供用户对象和GDI对象使用。进程在首次

初始化时,该句柄表为空。句柄表是一个由数据结构组成的数组,包含一个内核对象指针,一个访问掩码和一些标志。系统用索引来

标识内核对象的信息保存在进程句柄表中的具体位置,句柄值除以4即为索引值(针对不同版本的windows系统可能不同)。

2. 要注意内核对象返回值INVALID_HANDLE_VALUE(-1)和NULL(0)的区别,凡是用于创建内核对象的函数,在检查它们的返回值时

务必相当仔细。一般只有CreateFile才返回INVALID_HANDLE_VALUE。

3. 对于内核对象,操作系统执行操作过程为:进程终止时,系统会自动扫描进程句柄表。如果这个表中有任何有效的记录项(即进程进程终

止前未关闭的对象),操作系统会为我们关闭这些对象句柄(只要这些对象的使用计数为0)。

4. 进程共享内核对象机制

(1). 对象句柄继承

a.创建内核对象时将SECURITY_ATTRIBUTES结构的bInheritHandle设为TRUE

b.将CreateProcess参数的bInheritHandle设为TRUE,注:对象句柄的继承只会在生成子进程的时候发生

c.子进程无法查看自己继承了任何的对象句柄,在开发阶段最好用文档表明

(2). 改变句柄标志

SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags)

两个关键标志HANDLE_FLAG_INHERIT和HANDLE_FLAG_PROTECT_FROM_CLOSE

(3). 为对象命名

许多(但不是全部)内核对象都可以进行命名

(4). 复制对象句柄

从一个进程的句柄表中获得一个记录项,然后在另一个进程的句柄表中创建这个记录项的副本(DuplicateHandle)

5. 实例

typedef HANDLE (_stdcall* BOUNDARYDESCRIPTOR)(LPTSTR, DWORD);
BOUNDARYDESCRIPTOR CreateBoundaryDescriptor;

typedef BOOL (_stdcall* ADDSIDTOBOUNDARYDESCRIPTOR)(HANDLE *, PSID);
ADDSIDTOBOUNDARYDESCRIPTOR AddSIDToBoundaryDescriptor;

typedef HANDLE (_stdcall *CREATEPRIVATENAMESPACE)(LPSECURITY_ATTRIBUTES, LPVOID, LPCSTR);
CREATEPRIVATENAMESPACE CreatePrivateNamespace;

typedef HANDLE (_stdcall* OPENPRIVATENAMESPACE)(PVOID, LPCSTR);
OPENPRIVATENAMESPACE OpenPrivateNamespace;

void CSingletonDlg::CheckInstances()
{
    HMODULE hKernel = LoadLibrary("Kernel32.dll");

    if (hKernel)
    {
        CreateBoundaryDescriptor = (BOUNDARYDESCRIPTOR)GetProcAddress(hKernel, "CreateBoundaryDescriptorA");
        // 注意函数名AddSIDToBoundaryDescriptor后面没带'A'
        AddSIDToBoundaryDescriptor = (ADDSIDTOBOUNDARYDESCRIPTOR)GetProcAddress(hKernel, "AddSIDToBoundaryDescriptor"); 

        CreatePrivateNamespace = (CREATEPRIVATENAMESPACE)GetProcAddress(hKernel, "CreatePrivateNamespaceA");
        OpenPrivateNamespace = (OPENPRIVATENAMESPACE)GetProcAddress(hKernel, "OpenPrivateNamespaceA");
    }

    );

    if (NULL == m_hBoundaryDescriptor)
    {
        // 创建边界描述符失败
        return;
    }

    // 为边界描述符关联一个SID
    BYTE localAdminSID[SECURITY_MAX_SID_SIZE];
    PSID pLocalAdminSID = &localAdminSID;
    DWORD cbSID = sizeof(localAdminSID);
    if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID))
    {
        // 创建SID失败
        return;
    }

    if (!AddSIDToBoundaryDescriptor(&m_hBoundaryDescriptor, pLocalAdminSID))
    {
        // 链接边界描述符失败
        return;
    }

    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.bInheritHandle = FALSE;
    if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
        TEXT("D:(A;;GA;;;BA)"), SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL))
    {
            return;
    }

    // 创建命名空间
    /*HANDLE */m_hPrivateNameSpace = CreatePrivateNamespace(&sa, m_hBoundaryDescriptor, "3-Namespace");
    LocalFree(sa.lpSecurityDescriptor);

    // 检查已创建的命名空间:看是否有权限访问ERROR_ACCESS_DENIED和该命名空间是否存在ERROR_ALREADY_EXISTS
    DWORD dwLastErro = GetLastError();
    if (m_hPrivateNameSpace == NULL)
    {
        if (ERROR_ACCESS_DENIED == dwLastErro)
        {
            return;
        }
        else if (ERROR_ALREADY_EXISTS == dwLastErro)
        {
            m_hPrivateNameSpace = OpenPrivateNamespace(m_hBoundaryDescriptor, "3-Namespace");

            if (NULL == m_hPrivateNameSpace)
            {
                return;
            }
        }
    }

    // 创建互斥对象
    };
    StringCchPrintf(cMutexName, sizeof(MAX_PATH), "%s\\%s", "3-Namespace", "Single");
    g_hSingleton = CreateMutex(NULL, FALSE, cMutexName);

    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        AddText(TEXT("Another instance of Singleton is running:\r\n"));
        AddText(TEXT("--> Impossible to access application features.\r\n"));
    } else
    {
        AddText(TEXT("First instance of Singleton:\r\n"));
        AddText(TEXT("--> Access application features now.\r\n"));
    }

    FreeLibrary(hKernel);
}

6. 附录

boundary 分界线;范围

descriptor 描述符

(1). HANDLE CreateBoundaryDescriptor(PCTSTR pszName, DWORD dwFlags)

创建边界描述符

第二个参数为以后保留,目前没什么用,为0

函数返回的并非是一个句柄,而是一个指向用户模式结构的指针,结构中包含了边界的定义,不要把返回的句柄值传给

CloseHandle,而要传给DeleteBoundaryDescriptor

(2). BOOL AddSIDToBoundaryDescriptor(HANDLE *phBoundaryDescriptor, PSID pRequireSid)

将一个特权用户组的SID与边界描述符关联起来

(3). HANDLE CreatePrivateNameSpace(PSECURITY_ATTRIBUTES, PVOID pvBoundaryDescriptor, pszAliasPrefix)

对创建好的命名空间进行检查GetLastError(),ERROR_ACCESS_DENIED、ERROR_ALREADY_EXISTS

(4). HANDLE OpenPrivateNameSpace( PVOID pvBoundaryDescriptor, pszAliasPrefix)

(5). ClosePrivateNameSpace(HANDLE hNamespace, DWORD dwFlags)

如果我们已经创建了命名空间,而且不希望它在关闭后仍然可见,可以用PRIVATE_NAMESPACE_FLAG_DESTRPY作为第二个参数,反之则为0

(6). BOOL WINAPI CreateWellKnowSid()

创建一个SID

参数1:WellKnowSidType SID类型,WELL_KNOW_SID_TYPE是枚举类型,它包含一系列的安全描述符类型

        参数2: _in_opt PSID DomainSid    指向创建了SID的域的指针,为NULL时表示本地计算机

参数3:_out_opt PSID pSid          指向存储SID地址

参数4: _inout DWORD *cbSid      指向存储pSid的大小的地址

Windows核心编程&内核对象的更多相关文章

  1. windows核心编程-信号量(semaphore)

    线程同步的方式主要有:临界区.互斥区.事件.信号量四种方式. 前边讲过了互斥器线程同步-----windows核心编程-互斥器(Mutexes),这章我来介绍一下信号量(semaphore)线程同步. ...

  2. windows核心编程---第八章 使用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  3. Windows核心编程学习九:利用内核对象进行线程同步

    注:源码为学习<Windows核心编程>的一些尝试,非原创.若能有助于一二访客,幸甚. 1.程序框架 #include "Queue.h" #include <t ...

  4. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  5. windows核心编程---第九章 同步设备IO与异步设备IO之同步IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

  6. 回忆读windows 核心编程

    看<windows 核心编程> 第五版到纤程了,下一章节即将介绍内存体系编程.如果做window平台下的开发,我感觉此书一定要读.记得开始讲解了window的基础,然后讲解内核对象.内核对 ...

  7. 《Windows核心编程》第5版 学习进度备忘

    学习资源:<Windows核心编程>第5版 知识基础支持: 本书与<Windows程序设计>第5版珍藏版结合很好,二者重叠内容不多,二者互补性强,而且相关方面的优秀书籍 跳过的 ...

  8. 【windows核心编程】 第八章 用户模式下的线程同步

    Windows核心编程 第八章 用户模式下的线程同步 1. 线程之间通信发生在以下两种情况: ①    需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性 ②    一个线程需要通知其他线程 ...

  9. 【windows核心编程】 第六章 线程基础

    Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ①    一个是线程的内核 ...

随机推荐

  1. http请求参数中包含特殊字符的严重后果,比如:#

    URL请求中不能包含特殊符号,比如:# 今天在调接口,突然发现接口参数中传递的数据没有完全接收到controller层的model模型中,反反复复测了好几遍,真不信这个邪了,头晕脑胀的时候才关注到UR ...

  2. UGUI 粒子特效与UI层级问题

    游戏中,界面上有些按钮之上需要放置一个特效,或者有些区域显示比如image上显示一个特效,这时候如果再打开一个UI,我们需要让新的UI显示在特效上层,而不是被特效遮挡,这是就需要设置特效的渲染顺序. ...

  3. 深入理解ES6之—符号与符号属性

    在js已有的基本类型(字符串,数值,布尔型,null和undefined)之外,es6引入了一种新的基本类型:==符号(Symbol)==.符号起初被设计用于创建对象私有成员. 符号没有字面量形式,你 ...

  4. es6 的循环

    for-of  循环 for-of 不能直接用来遍历对象的属性,如果你想遍历对象的属性,你可以使用 for-in 语句(for-in 就是用来干这个的),或者使用下面的方式: for (let key ...

  5. C#学习笔记-装饰模式

    题目:给顾客打扮,但是需要满足正常的穿衣风格,例如先穿了衬衣再打领带,最后在穿鞋子,这种基本要求. 分析: 现在将具体的衣服裤子和鞋子都分别写在不同的类里面,这样方便以后添加新的衣服,这些全部都属于服 ...

  6. sort()没有返回值

    aList = [1,3,5,2,4] aList = aList.sort() print(aList) None

  7. 使用jemeter手工编写注册、登陆脚本 运用 fiddler (三)

    手工注册脚本的录制 我们可以发现  编写手工脚本 需要用到两个软件一起合作才能编写成功 工作亦是如此  养成良好的团队合作精神 是必不可少的 现在我们来看手工编写注册脚本 第一步 :我们先进入网站首页 ...

  8. Django----->一周后的重温

    Django   一 什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来 ...

  9. MyEclipse10.7 10.6导出war文件报错 “SECURITY ALERT: INTEGERITY CHECK ERROR”

    myeclipse10.7的版本破解后,导出war包时报“SECURITY ALERT: INTEGERITY CHECK ERROR”的错误. 经过测试本次破解可以解决此错误,只需替换一个jar文件 ...

  10. vim中的批量替换

    VI中的批量替换   1) 文件内全部替换:   :%s#abc#123#g (如文件内有#,可用/替换,:%s/abc/123/g)     --注:把abc替换成123   (或者: %s/str ...