Dissecting the Windows Kernel - 关于ObReferenceObjectByHandle中对句柄的处理
来源:互联网 发布:网络通信加密系统 编辑:程序博客网 时间:2024/06/04 19:04
Dissecting the Windows Kernel
- 关于ObReferenceObjectByHandle中对句柄的处理
一、 摘要
在开发一个 Anti-Rootkit工具中的枚举进程句柄时,发现枚举System Process进程的句柄信息在Windows XP和Windows 7/8的处理细节有一些不一样,遂想刨根问底,一探究竟。在获取句柄对象名和类型名的时候都会使用NtQueryObject函数,而NtQueryObject函数中是通过ObReferenceObjectByHandle根据Handle获得Object的。此时内核句柄的所有秘密都在ObReferenceObjectByHandle中。
二、 相关补充
2.1 ETHREAD或KTHREAD中的EPROCESS域
l ETHREAD中的域ThreadsProcess(PEPROCESS)在Windows XP中有,Windows 7/8中没有。指向当前线程所属的进程,这是在线程初始创建时赋值的。通过此域,可以很方便地从一个线程访问到它所属的进程。WRK中base\ntos\inc\Ps.h中的宏THREAD_TO_PROCESS据此实现:
#define THREAD_TO_PROCESS(Thread) ((Thread)->ThreadsProcess)
l KTHREAD中Process(PKPROCESS)在Windows 7/8中有,Windows XP中没有,但本人也没有找到该域的作用。如有知情人,请告知。但我猜测作用同上。
注:以上两个域在WRK-1.2中全都存在。
l KTHREAD中的ApcState(KAPC_STATE)中的Process(KPROCESS)也可以EPROCESS。PsGetCurrentProcess函数就是据此实现:
#define _PsGetCurrentProcess()\
(CONTAINING_RECORD(\
((KeGetCurrentThread())->ApcState.Process),\
EPROCESS,Pcb))
这个域会根据线程所属的进程动态变化。当当前线程Attach到其他进程中时(KeAttachProcess/KeStackAttachProcess),原线程中的ApcState保存到SavedApcState中,ApcState保存新进程环境中的信息,ApcState.Process就指向新进程的EPROCESS,所以这里获取的才是真正的当前EPROCESS。Detach到原先进程中时(KeDetachProcess/KeUnstackDetachProcess),该过程与前一步相反。这里可参考WRK中相关API的实现。
三、 ObReferencObjectByHandle在不同版本Windows实现差异
3.1 Windows XP x86
虽然WRK是从Windows XP AMD64和Windows Server 2003 SP1抓取出来的,但ObReferenceObjectByHandle的处理结果和32位XP是以一样,所以这里以WRK中的源码为例,这样更为清晰。
//
// 摘自:wrk-1.2\base\ntos\ob\Obref.c
//
NTSTATUS
ObReferenceObjectByHandle (
__inHANDLEHandle,
__inACCESS_MASKDesiredAccess,
__in_optPOBJECT_TYPEObjectType,
__inKPROCESSOR_MODEAccessMode,
__outPVOID *Object,
__out_optPOBJECT_HANDLE_INFORMATIONHandleInformation
)
{
ACCESS_MASKGrantedAccess;
PHANDLE_TABLE HandleTable;
POBJECT_HEADER ObjectHeader;
PHANDLE_TABLE_ENTRY ObjectTableEntry;
PEPROCESSProcess;
NTSTATUSStatus;
PETHREADThread;
ObpValidateIrql("ObReferenceObjectByHandle");
Thread= PsGetCurrentThread ();
*Object= NULL;
//
// 检查Handle是不是内核句柄
// (即小于0,也就是最高位带KERNEL_HANDLE_MASK标识)。
// 这里有两个句柄要区别处理:
// 当前进程句柄-1(0xFFFFFFFF)和当前线程句柄-2(0xFFFFFFFE)。
//
if((LONG)(ULONG_PTR)Handle < 0) {
if(Handle ==NtCurrentProcess()){
if((ObjectType ==PsProcessType)
|| (ObjectType ==NULL)){
Process = PsGetCurrentProcessByThread(Thread);
GrantedAccess =Process->GrantedAccess;
if ((SeComputeDeniedAccesses(
GrantedAccess,DesiredAccess)== 0) ||
(AccessMode ==KernelMode)){
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);
if (ARGUMENT_PRESENT(HandleInformation)) {
HandleInformation->GrantedAccess
= GrantedAccess;
HandleInformation->HandleAttributes= 0;
}
ObpIncrPointerCount(ObjectHeader);
*Object =Process;
ASSERT( *Object!=NULL );
Status =STATUS_SUCCESS;
} else {
Status =STATUS_ACCESS_DENIED;
}
} else {
Status =STATUS_OBJECT_TYPE_MISMATCH;
}
returnStatus;
} elseif (Handle==NtCurrentThread()) {
if((ObjectType ==PsThreadType)
|| (ObjectType ==NULL)){
GrantedAccess =Thread->GrantedAccess;
if ((SeComputeDeniedAccesses(
GrantedAccess,DesiredAccess)== 0) ||
(AccessMode ==KernelMode)){
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);
if (ARGUMENT_PRESENT(HandleInformation)) {
HandleInformation->GrantedAccess=GrantedAccess;
HandleInformation->HandleAttributes= 0;
}
ObpIncrPointerCount(ObjectHeader);
*Object =Thread;
ASSERT( *Object!=NULL );
Status =STATUS_SUCCESS;
} else {
Status =STATUS_ACCESS_DENIED;
}
} else {
Status =STATUS_OBJECT_TYPE_MISMATCH;
}
returnStatus;
} elseif (AccessMode==KernelMode) {
//
//这里才是真正处理内核句柄的地方。
//注意:内核句柄只能在内核模式访问。
//#define KERNEL_HANDLE_MASK\
// ((ULONG_PTR)((LONG)0x80000000))
//#define EncodeKernelHandle(H) \
// (HANDLE)(KERNEL_HANDLE_MASK | (ULONG_PTR)(H))
//#define DecodeKernelHandle(H) \
// (HANDLE)(KERNEL_HANDLE_MASK ^ (ULONG_PTR)(H))
//内核句柄是还KERNEL_HANDLE_MASK标识的(即最高位为1)。
//记住,这里的Handle不是一个真正的Handle,
// 在使用前必须转化成正规Handle。
//这里的DecodeKernelHandle就是将最高位的标识去除。
//
Handle =DecodeKernelHandle(Handle );
//
// 这里得到的句柄表就是全局的内核句柄表ObpKernelHandleTable。
//
HandleTable = ObpKernelHandleTable;
} else{
//
//The previous mode was user for this kernel handle value.
//Reject it here.
//
returnSTATUS_INVALID_HANDLE;
}
} else{
//
// 如果Handle大于0(即没有KERNEL_HANDLE_MASK标识),
// 说明不是System进程的句柄,
// 就从当前ETHREAD得到EPROCESS,从而得到HandleTable。
//
//#definePsGetCurrentProcessByThread(xCurrentThread) \
// (ASSERT((xCurrentThread) == PsGetCurrentThread()),\
// CONTAINING_RECORD(\
// ((xCurrentThread)->Tcb.ApcState.Process),\
// EPROCESS,Pcb))
//
HandleTable= PsGetCurrentProcessByThread(Thread)->ObjectTable;
}
//
// 这之后便是在得到的HandleTable里查找ObjectHeader和Object。
//
// 以下代码引用省略……
//
returnStatus;
}
从以上代码可以看出:在Windows XP 32位下无论从何种途径获取到的内核句柄(最高位有或没有KERNEL_HANDLE_MASK标识),在正确Attach到内核句柄和内核句柄表所在的System 进程,通过ObReferenceObjectByHandle都能得到正确的对象。正确带KERNEL_HANDLE_MASK标识的句柄当然没有问题,但没有该标识的句柄因为Attach到System进程以后,是通过ApcState.Process来获取HandleTable的,此时的当前进程就是System进程,所以也可以得到正确的对象。
3.2 Windows 7 32-Bit
此处实际调用_ObReferenceObjectByHandleWithTag。
代码比较多,将图放大即可看清。
注:
1.图中以数字1, 2, 3……为序号的流程是输入正确的内核句柄(带KERNEL_HANDLE_TABLE)后查找HandleTable的处理逻辑。
2.图中以大写字母A, B, C ……为序号的流程是输入普通句柄后查找HandleTable的处理逻辑。
在Windows7 32位下,在Attach到System进程之后,只有在输入正确(带KERNEL_HANDLE_MASK标识)的句柄之后,调用ObReferenceObjectByHandle才会得到正确的对象。从IDA分析中可看出,当句柄不是正确的内核句柄时,如果得到的句柄表却是内核句柄表(ApcState.Process),则返回0xC0000008(STATUS_INVALID_HANDLE),这里对Handle做了正确性验证,更为安全。
3.3 NOTE
一、逆向ObpCreateHandle可知:内核中创建或打开句柄时,如果是内核句柄,则获取得到的句柄都是带KERNEL_HANDLE_TABLE标识的,但内核句柄在内核句柄表里是没有该标识的。也就是该标识只是对调用者用于区别,在内部实现和保存时和其它句柄没有区别。
二、Windows 7/8 64位下KERNEL_HANDLE_MASK为0xFFFFFFFF80000000。
在Windows 7 64位和Windows 8 32/64中ObReferenceObjectByHandle查找HandleTable的处理过程和Windows 7 32位是一样的。
四、 参考
1. 毛德操. Windows内核情景分析
2. 潘爱民. Windows内核原理与实现
3. WRK-1.2
- Dissecting the Windows Kernel - 关于ObReferenceObjectByHandle中对句柄的处理
- 关于windows句柄的含义
- C++中关于Windows窗口句柄的相关接口
- KERNEL中MCE处理流程(一) - 关于PR_MCE_KILL_EARLY的处理 (基于Kernel 4.3-rc3)
- 我对windows句柄的理解
- Dissecting the Camera Matrix
- 为什么Windows那么多以On开头的方法?(关于对继承中事件处理的认识!)
- 关于windows句柄机制的一些心得体会
- QTP中关于对webelement的处理。
- ObReferenceObjectByHandle
- 关于windows句柄
- 关于windows 伪句柄
- win32中对“句柄”的理解
- 关于new等函数对任务管理器中内存、句柄数变化的影响
- delphi中没有窗口句柄的控件如何处理windows消息
- 探讨Windows编程中句柄的本质
- Windows中句柄和ID的区别
- 【Windows】DLL中获取自身的句柄
- vmware fusion NAT下的端口,ip配置
- select实现tcp并发服务器的基本框架流程
- 论Java的ArrayList.add(e)和C++的vector.push_back(e)的区别
- 11G RAC 裸设备表空间迁移到ASM
- 领域模型驱动设计(Domain Driven Design)入门概述
- Dissecting the Windows Kernel - 关于ObReferenceObjectByHandle中对句柄的处理
- 使用命令行对APK签名(已有签名文件)
- [MySQL] 索引与性能(2)- 聚簇索引
- java反射中getMethod getDeclaredMethod .
- UIAlertView用法
- Piotr Dollar(皮洽-多拉)同学的论文Peestrain Detection:An Evaluation of the State of the Art读后记录
- Block语法的使用
- 程序员练级攻略
- PHP执行系统命令 exec,system,passthru,popen