《windows核心编程》第3章 内核对象-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、内核对象简介

1.1 内核对象有哪些

令牌对象 token、事件对象 Event、文件对象 File、文件映射对象 Mapping_File、线程对象 Thread、时钟对象 Timer、线程池对象 ThreadPool、I/O完成端口对象 Completion port、工作对象 job、邮槽对象 mailslot、互斥对象 Mutex、管道对象 pipe、进程对象 process、信号灯对象 semaphore

1.2 内核对象简介

每个进程创建的内核对象是一个索引通过这个索引会在每个进程的内核对象表中找到这个内核对象的内存块同一个内核对象在不同进程中的句柄是不一样的

内核对象用一个句柄来标识。

内核对象的内存块位于操作系统的内核空间应用程序不能直接操作内核对象需要系统给定的函数来操作

内核对象的结构公用部分安全描述符、计数和个性部分

1.3 内核对象计数的作用

如果一个内核对象能被多个进程共用。每当一个进程使用这个内核对象的时候内核对象计数值就加1每当有一个进程不使用这个内核对象的时候内核对象计数值就减1。当计数值为0的时候系统会释放这个内核对象资源

1.4 区分内核对象和用户对象

区分应用层对象和内核对象是看创建对象的函数如果创建对象的函数有安全描述符那么这个函数创建的对象就是内核对象

内核对象

HANDLE CreateFileMapping(
    HANDLE hFile,                       //物理文件句柄
    LPSECURITY_ATTRIBUTES lpAttributes, //安全设置
    DWORD flProtect,                    //保护设置
    DWORD dwMaximumSizeHigh,            //高位文件大小
    DWORD dwMaximumSizeLow,             //低位文件大小
    LPCTSTR lpName                      //共享内存名称
);

应用层对象

BOOL CreateBitmap(
    int nWidth,          // 指定位图的宽度(以像素为单位)
    int nHeight,         // 指定位图的高度(以像素为单位)
    UINT nPlanes,        // 指定位图中的颜色平面的数量
    UINT nBitcount,      // 指定每个显示像素的颜色位的数量
    const void* lpBits   // 指向包含的出师未土值的字节的数组。如果为NULL,则不初始化新图
);

1.5 继承选项的含义

创建进程内核

BOOL CreateProcess(  
 LPCTSTR lpApplicationName,                 // 应用程序名称  
 LPTSTR lpCommandLine,                      // 命令行字符串  
 LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程的安全属性  
 LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 线程的安全属性  
 BOOL bInheritHandles,                      // 是否继承父进程的属性  
 DWORD dwCreationFlags,                     // 创建标志  
 LPVOID lpEnvironment,                      // 指向新的环境块的指针  
 LPCTSTR lpCurrentDirectory,                // 指向当前目录名的指针  
 LPSTARTUPINFO lpStartupInfo,               // 传递给新进程的信息  
 LPPROCESS_INFORMATION lpProcessInformation // 新进程返回的信息  
);

如果上面创建进程内核函数中的继承选项传入了TRUE父进程在创建子进程的时候会把内核对象继承给他。也就是子进程也有父进程的内核对象

二、内核对象操作

2.1 关闭内核对象

CloseHandle(HANDLE);

2.2 内核对象信息获取与修改

1、修改内核对象信息

BOOL SetHandleInformation(
  [in] HANDLE hObject,
  [in] DWORD  dwMask,
  [in] DWORD  dwFlags
);

[in] dwMask

一个掩码指定要更改的位标志。 使用 dwFlags 说明中显示的相同常量。

[in] dwFlags

指定对象句柄的属性的位标志集。 此参数可以是 0 或以下一个或多个值。

含义

HANDLE_FLAG_INHERIT

0x00000001

如果设置了此标志则使用 CreateProcess 设置为 TRUE 的 bInheritHandles 参数创建的子进程将继承对象句柄。

HANDLE_FLAG_PROTECT_FROM_CLOSE

0x00000002

如果设置了此标志则调用 CloseHandle 函数不会关闭对象句柄。

2、获取内核信息

BOOL GetHandleInformation(
  [in]  HANDLE  hObject,
  [out] LPDWORD lpdwFlags
);

函数的使用

GetHandleInformation(handle, &dw);

if (dw & HANDLE_FLAG_INHERIT)
{
        该对象可以被继承
​​​​​​​}

三、防止多开的程序

3.1 命名内核对象

命名内核对象主要是为了进程间共享内核对象。创建内核对象的函数中如果有pszName参数说明这个内核对象可以被命名也就是可以创建命名内核对象。命名内核对象可以在不同进程中使用

3.2 用互斥的内核对象实现防止多开程序

#include <Windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <Windows.h>
#include <tchar.h>

int _tmain()
{
	HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("huan"));	
												// 创建了一个名字叫huan的互斥内核对象
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		_tprintf(TEXT("Can't open this program.\n"));
		_gettchar();
		return 0;
	}
	DWORD sessionid = 0;
	ProcessIdToSessionId(GetProcessId(NULL), &sessionid);

	_tprintf(TEXT("%s,\n"), TEXT("this is the first instance! and session id is"));

	_gettchar();

	return 0;
}

3.3 破解防止软件多开

方法1反汇编修改判定 if (GetLastError() == ERROR_ALREADY_EXISTS) 这个判定条件让这个条件不成立

方法2使用Process explorer进程资源管理器删除内核

官方下载链接进程资源管理器 - Sysinternals | Microsoft Learn

1、以管理员权限打开软件

2、显示进程内核对象

3、关闭内核对象句柄

注意我们编写的软件可以轻松列举其他进程用到的所有内核对象但是想要实现杀死内核对象很困难首先要注入到所有用到内核对象的程序然后关闭内核对象最终杀死内核对象。而微软内部员工有源码所以编写上面的程序相比比较轻松

四、session会话

4.1 简介

windows还没人登录的时候就会创建一个session0会话windows服务程序在session0中。每当有一个用户登录就会创建一个session

4.2 查看当前程序运行在哪个session中

获取当前进程句柄根据句柄获取当前进程id根据id获取程序运行在哪个会话中获取成功返回非0

#include <Windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <Windows.h>
#include <tchar.h>

int _tmain()
{
	HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("huan"));	// 创建了一个名字叫huan的互斥内核对象
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		_tprintf(TEXT("Can't open this program.\n"));
		_gettchar();
		return 0;
	}

	DWORD sessionid = 0;
	DWORD processid = 0;

	processid = GetProcessId(GetCurrentProcess());
	ProcessIdToSessionId(processid, &sessionid);	
	if (ProcessIdToSessionId(processid, &sessionid)) // 获取当前程序运行在哪个session会话中
		_tprintf(TEXT("%s, %d\n"), TEXT("this is the first instance! and session id is"), sessionid);

	_gettchar();

	return 0;
}

4.2 会话和命名空间

第1部分 全局命名空间

一个远程桌面的服务可以为自己的命名内核对象设置多个命名空间这些内核对象包括event、sernaphores、waitable timers、file-mapping、job object。在client/server类型的应用程序中服务都有一个全局命名空间global namespace。除此之外每个客户会话都可以有自己单独的命名空间来防止自己独有的内核对象。

独立的客户会话命名空间能够让多个客户运行同一个应用程序而不互相干扰。在一个话中使用使用会话名称作为内核对象的缺省命名空间 另外进程还可以使用全局命名空间使用的方法是在内核对象的名字前加上"Global\" 前缀

通俗说就是程序命名前面不加global前缀其他的会话就访问不到你会话中的内核对象全局命名空间内核对象创建方法如下

CreateEvent(NULL, FALSE, FALSE, "Global\\CSAPP");

第2部分 本地命名空间

如果要在本会话下创建一个命名对象可以内核对象前加上“Local\”前缀注意关键字是大小写敏感的。"Session\"前缀被系统保留不要使用这个前缀来创建命名内核对象

第3部分 私有命名空间

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: windows