Windows句柄表学习笔记 —— 句柄表全局句柄表
Windows句柄表学习笔记 —— 句柄表&全局句柄表
- 句柄表
- 实验一:在WinDbg中查看句柄表
- 第一步:打开一个Win32窗口程序
- 第二步:编译并运行以下代码
- 第三步:查看运行结果
- 第四步:在WinDbg中定位句柄表
- 句柄表结构
- 实验二:在WinDbg中查看并分析句柄表结构
- 第一步:打开一个Win32窗口程序
- 第二步:编译以下代码并运行
- 第三步:查看运行结果
- 第四步:在WinDbg中定位最后一个句柄
- 第五步:通过句柄定位内核对象
- 全局句柄表
- 全局句柄表结构
- 实验:在WinDbg中观察全局句柄表
- 第一步:打开一个Win32窗口程序
- 第二步:获得进程PID
- 第三步:获得全局句柄表
- 第四步:查看_EPROCESS结构体
句柄表
描述:
- 当一个进程创建或者打开一个内核对象时,将获得一个句柄,通过这个句柄可以访问对应的内核对象
- 句柄表存储在零环,一个进程使用了几个句柄,在该进程的句柄表中就会存储几个句柄
- 所有的句柄所对应的内核对象,都包含在
_OBJECT_HEADER
中,真正的内核对象保存在_OBJECT_HEADER +0x018 body
的位置
例如:
HANDLE g_hMutex = ::CreateMutex(NULL,FALSE, "XYZ");
HANDLE g_hMutex = ::OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");
HANDLE g_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE g_hThread = ::CreateThread(NULL, 0, Proc, NULL, 0, NULL);
思考:为什么微软不直接把内核对象的地址返回给用户,而是通过句柄的形式
答案:句柄存在的目的是为了避免在应用层直接修改内核对象,如果直接返回内核对象的地址,那么就意味着我们可以在应用层修改这个地址,一旦指向了无效的内核内存地址就会蓝屏
注意:
- 窗口、字体、笔刷等句柄与本章所学句柄是两码事
- 创建句柄不等同于打开句柄,当创建的时候,操作系统会在零环为内核对象分配一个结构体(例如CreateEvent),如果自己或他人打开了这个内核对象(例如OpenProcess),那么将不会再次为这个内核对象分配一个结构体,而是返回一个句柄的索引值
- 若同一个内核对象被引用了100次,那么在句柄表中就会存储100个内核对象的地址
- 句柄的值并非如上图所显示的只占4个字节,而是占8个字节,但是句柄表的值仍然按照4个字节进行计算
句柄索引定位对应句柄的计算公式:handle = index / 4 * 8
实验一:在WinDbg中查看句柄表
第一步:打开一个Win32窗口程序
本实验选择进程为计算器(calc.exe)
第二步:编译并运行以下代码
#include <stdio.h>
#include <windows.h>int main()
{DWORD PID;HANDLE hPro = NULL;HWND hWnd = ::FindWindow(NULL, "计算器");::GetWindowThreadProcessId(hWnd, &PID);for(int i=0; i<100 ;i++){hPro = ::OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, TRUE, PID);printf("句柄:%x\n", hPro);}getchar();return 0;
}
第三步:查看运行结果
第四步:在WinDbg中定位句柄表
kd> !process 0 0
kd> dt _EPROCESS 862adda0
kd> dt _HANDLE_TABLE 0xe12ca370
kd> dd 0xe1b6f000
第一个句柄地址 = 0xe1b6f000 + 0x7cc / 4 * 8
= 0xe1b6ff98
kd> dq 0xe1b6ff98
句柄表结构
1:共两个字节,低字节保留恒为0,高位字节是给SetHandleInformation这个函数用的,比如写成SethandleInformation(Handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE)
,那么这个位置将被写入0x02(HANDLE_FLAG_PROTECT_FROM_CLOSE
宏的值为0x00000002,取最低字节,最终这块是0x0200
)
2:这块是访问掩码,是给OpenProess这个函数用的
OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId)
具体存的就是这个函数第一个参数的值
3&4:共4个字节,其中bit0和bit2存储句柄的属性,默认为0,bit1表示该句柄是否可继承(参考OpenProcess的第二个参数)
实验二:在WinDbg中查看并分析句柄表结构
第一步:打开一个Win32窗口程序
本实验选择进程为计算器(calc.exe)
第二步:编译以下代码并运行
#include <stdio.h>
#include <windows.h>int main()
{DWORD PID;HANDLE hPro = NULL;HWND hWnd = ::FindWindow(NULL, "计算器");::GetWindowThreadProcessId(hWnd, &PID);for(int i=0; i<100 ;i++){hPro = ::OpenProcess(PROCESS_CREATE_THREAD, TRUE, PID);printf("句柄:%x\n", hPro);}//HANDLE_FLAG_PROTECT_FROM_CLOSE:句柄不可用CloseHandle关闭SetHandleInformation(hPro, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);getchar();return 0;
}
第三步:查看运行结果
第四步:在WinDbg中定位最后一个句柄
定位过程参考实验一
第五步:通过句柄定位内核对象
句柄值:0200003a 8617c7e3
清理后三位bit,后四个字节为地址: 8617c7e0
注意:此时该地址指向 _OBJECT_HEADER ,若想查看真正的内核对象结构体还要加上0x18
实验中我们使用OpenProcess打开句柄,因此查看_EPROCESS结构体
kd> dt _EPROCESS 8617c7e0 + 0x18
全局句柄表
描述:
- 进程的句柄表是私有的,每个进程都有一个自己的句柄表
- 除此之外,系统还有一个全局句柄表:
PsdCidTable
,为 _HANDLE_TABLE 结构,所有的进程和线程无论无论是否打开,都在这个表中 - 每个进程和线程都有一个唯一的编号:PID和CID 这两个值其实就是全局句柄表中的索引
- 进程和线程的查询,主要是以下三个函数,按照给定的PID或CID从PspCidTable从查找相应的进线程对象:
PsLookupProcessThreadByCid()
PsLookupProcessByProcessId()
PsLookupThreadByThreadId()
全局句柄表结构
描述:
- 若TableCode的值为0,说明
handle_max = 4KB / 8 = 512
,此时,句柄表为一级句柄表 - 若打开句柄数量超过512个,TableCode的值就会置为1,此时,句柄表变为两级句柄表。第一级句柄表中每个成员存储着第二级句柄表的地址,第二级句柄表中才真正存储着内核对象的地址,
handle_max = 1024 * 512
- 若打开句柄数量超过1024 * 512个,TableCode的值就会置为3,此时,句柄表变为三级句柄表,第一级为地址,第二级也为地址,第三级才是句柄,
handle_max = 1024 * 1024 * 512
实验:在WinDbg中观察全局句柄表
第一步:打开一个Win32窗口程序
本实验选择进程为计算器(calc.exe)
第二步:获得进程PID
index = 1048 / 4 = 0x106
第三步:获得全局句柄表
kd> dd PspCidTable
kd> dt _HANDLE_TABLE e1003c58
第四步:查看_EPROCESS结构体
kd> dq e1005000 + 0x106*8
kd> dt _EPROCESS 85e2d540 //后三位属性位清零
Windows句柄表学习笔记 —— 句柄表全局句柄表相关推荐
- Windows保护模式学习笔记(一)—— 段寄存器GDT表
Windows保护模式学习笔记(一)-- 段寄存器&GDT表 保护模式 参考书籍: 一.段寄存器 段寄存器的结构 段寄存器的读写 段寄存器的属性 1)探测Attribute: 2)探测Base ...
- Windows系统调用学习笔记(四)—— 系统服务表SSDT
Windows系统调用学习笔记(四)-- 系统服务表&SSDT 要点回顾 系统服务表 实验:分析 KiSystemService 与 KiFastCallEntry 共同代码 SSDT 实验: ...
- Windows保护模式学习笔记(八)—— 页目录表基址/页表基址
Windows保护模式学习笔记(八)-- 页目录表基址/页表基址 要点回顾 一.页目录表基址 实验:拆分线性地址C0300000,并查看其对应的物理页 第一步:打开一个进程,获得它的Cr3 第二步:查 ...
- oracle查看表空间的内容,学习笔记:Oracle查看object对象 表空间 表 索引 数据文件的使用空间...
天萃荷净 运维DBA咨询想要查看Oracle的object对象的使用空间大小,包括表空间 表 索引 数据文件的使用空间 1.查看Oracle表空间大小 Select Tablespace_Name,S ...
- MySQL学习笔记-约束以及修改数据表
MySQL学习笔记-约束以及修改数据表 约束: 按功能划为: NOT NULL , PRIMARY KEY , UNIQUE KEY , DEFAULT , FOREIGN KEY 按数据列的数目划为 ...
- Windows消息机制学习笔记(二)—— 窗口与线程
Windows消息机制学习笔记(二)-- 窗口与线程 要点回顾 消息从哪里来? 实验一:Spy++捕获消息 实验二:消息捕获 消息到哪里去? 窗口在哪? 实验:分析CreateWindowExW 窗口 ...
- Windows消息机制学习笔记(三)—— 消息的接收与分发
Windows消息机制学习笔记(三)-- 消息的接收与分发 要点回顾 消息循环 消息队列 消息的接收 GetMessage 实验1:理解GetMessage 第一步:编译并运行程序A 第二步:编译并运 ...
- Windows事件等待学习笔记(三)—— WaitForSingleObject函数分析
Windows事件等待学习笔记(三)-- WaitForSingleObject函数分析 要点回顾 WaitForSingleObject NtWaitForSingleObject KeWaitFo ...
- Windows保护模式学习笔记(十)—— TLB
Windows保护模式学习笔记(十)-- TLB 地址解析 10-10-12分页 2-9-9-12分页 TLB TLB结构 TLB种类 练习1:体验TLB的存在 第一步:运行代码 第二步:设置中断门描 ...
最新文章
- 腾讯十周年,看看你的QQ是什么时候注册的?
- MariaDB 加密特性及使用方法
- 【labelme】改造labelme
- [Jobdu] 题目1499:项目安排
- python菱形画法解释_用Python画棱形
- 想让马云成为你的老大?揭秘阿里面试情景
- 15 MM配置-BP业务伙伴-定义供应商主记录的编号范围
- vscode创建代码截图_如何在VSCode中创建代码配置文件
- 如何判断一个大佬值不值得跟随
- python画图颜色代码rgb_python – matplotlib 3D散点图,其标记颜色对应于RGB值
- python判断变量相等_Python判断两个对象相等的原理
- 请简要概括linux与windows在文件系统方面存在的不同点,简要回答下列与网络操作系统、网络安全和数据存储相关的问题,将解答填入答题纸对应栏内。br 【问题1】(10分)br (a)Win...
- Vue中导出Excel
- Qt入门教程【高级控件篇】QTreeView树形视图
- 抖音怎么投放广告,抖音广告投放效果
- 用python自动制作ppt——第三讲——插入文本框
- 工具说明书 - FTDI芯片的USB转UART串口线
- 川大 计算机学院 惠子,“挑战杯”四川大学2013年学生科技节之“纺兴未艾,织行天下”废物循环利用大赛决赛圆满落幕...
- Python爬取网易云音乐评论,反爬算啥啊!
- python爬虫win10程序_Python爬虫教程:批量提取Win10锁屏壁纸
热门文章
- Appendix之setup.py:附录文件内容记录setup.py
- Py之pyquery:pyquery的简介、安装、使用方法之详细攻略
- ML与math:机器学习与高等数学基础概念、代码实现、案例应用之详细攻略——基础篇
- pyhanlp 分词与词性标注
- php 数组元素快速去重
- cdh5.13.1 升/降级SPARK2 (parcel安装的同理)
- 【UVA 437】The Tower of Babylon(记忆化搜索写法)
- Java实现找出数组中重复次数最多的元素以及个数
- 砝码问题之一(回头发现貌似多重背包)
- __declspec(dllexport)的作用