扫描系统句柄表(WIN7 x86)(附录源码)

PspCidTable存放着系统中所有的进程和线程对象,其索引也就是进程ID(PID)或线程ID(TID).先通过它来看看windbg里的HANDLE_TABLE结构:

可以看到地址 0x83f41bc4中存放的内容是 0x 8da010a8,这是系统的_HANDLE_TABLE的结构。

好了,现在windbg是得到HANDLE_TABLE结构了,还是要代码实现的。这里只简单用一下加偏移:

//system进程的eprocess地址

PEPROCESS EProcess = (PEPROCESS)0x86aee798;

#define  _HANDLE_TABLE_OFFSET_EPROCESS  0x0f4

HandleTable = (PHANDLE_TABLE)(*((ULONG*)((UINT8*)EProcess + _HANDLE_TABLE_OFFSET_EPROCESS)));

HANDLE_TABLE这里有两个需要关注的内容,第一个是TableCode,另一个是NextHandleNeedingPool.

TableCode成员,可以认为它是一个指向句柄表的地址,其中这个数值的低2位表示的是句柄表的层数,所以我们实际得到的句柄表的地址是要掩掉低2位的,也就是 TableCode&& 0xFFFFFFF8(TableCode&~0x3) ,其中低两位 为0时表示  1层索引,为1时表示2层索引,为2时表示3层索引,最后我们索引到的是一个_HANDLE_TABLE_ENTRY的结构,这个结构里面有我们要的_EPROCESS对象地址。

借某位博主图一用(图片源自:http://www.cnblogs.com/ck1020/p/5897460.html

这个图可以说是非常清晰了(有一处小错误,TableCode低两位为2是三级索引,不过瑕不掩瑜呐)

对于每一个索引表大小都为1页 4KB,其中一级表存放的是8Byte的_HANDLE_TABLE_ENTRY的结构,所以每一个1级表就只能存放512个项;

2级表存放的是1级表的地址(4Byte)那么每一个2级表能够存放4KB/4B = 1024个1级表的地址,如果存在3级表的话,相当庞大的数目了。

回到上面可以看到TableCode为0x8f71001, 低2位为01 ,可以知道当前是2层索引结构,其中句柄表的地址为0x8f71000,通过命令 dd 0x0x8f71000看到对应的2级索引表:

可以看到就只有两项,也就是说有两个1级表,那么当前的句柄表能够容纳512*2=1024个句柄.

通过地址0x8da04000访问到第一个1级索引表来看看 :

里面是很多8字节的_HANDLE_TABLE_ENTRY,查看WRK的源码看它的结构:

typedef struct _HANDLE_TABLE_ENTRY {

//
// The pointer to the object overloaded with three ob attributes bits in
// the lower order and the high bit to denote locked or unlocked entries
//

union {

PVOID Object;

ULONG ObAttributes;

PHANDLE_TABLE_ENTRY_INFO InfoTable;

ULONG_PTR Value;
};

//
// This field either contains the granted access mask for the handle or an
// ob variation that also stores the same information. Or in the case of
// a free entry the field stores the index for the next free entry in the
// free list. This is like a FAT chain, and is used instead of pointers
// to make table duplication easier, because the entries can just be
// copied without needing to modify pointers.
//

union {

union {

ACCESS_MASK GrantedAccess;

struct {

USHORT GrantedAccessIndex;
USHORT CreatorBackTraceIndex;
};
};

LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

typedef struct _HANDLE_TABLE_ENTRY {

//

//  The pointer to the object overloaded with three ob attributes bits in

//  the lower order and the high bit to denote locked or unlocked entries

//

union {

PVOID Object;

ULONG ObAttributes;

PHANDLE_TABLE_ENTRY_INFO InfoTable;

ULONG_PTR Value;

};

//

//  This field either contains the granted access mask for the handle or an

//  ob variation that also stores the same information.  Or in the case of

//  a free entry the field stores the index for the next free entry in the

//  free list.  This is like a FAT chain, and is used instead of pointers

//  to make table duplication easier, because the entries can just be

//  copied without needing to modify pointers.

//

union {

union {

ACCESS_MASK GrantedAccess;

struct {

USHORT GrantedAccessIndex;

USHORT CreatorBackTraceIndex;

};

};

LONG NextFreeTableEntry;

};

} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

通过这个结构可以看出HANDLE_TABLE_ENTRY 8Byte的前4Byte是一个Object对象,这也就是我们要找的_EPROCESS指针,但是需要注意的是,句柄表中的Object指针的低3位则是有另外意义的:

①第0位OBJ_PROTECT_CLOSE,表示调用者是否允许关闭该句柄;

②第1位OBJ_ INHERIT,指示该进程所创建的子进程是否可以继承该句柄,即是否将该句柄项拷贝到子进程的句柄表中;

③第2位OBJ_ AUDIT_OBJECT_ CLOSE。指示关闭该对象时是否产生一个审计事件。

所以我们在使用该指针的时候要掩掉低3Bit

也就是说对于Object=86ae88a9应该变为ObjectHeader=0x86aee799& 0xFFFFFFF8 (0x86aee799&~0x07)&~0x07 = 0x86aee798,这才是需要的_EPROCESS 的地址;

说了这么多,我们的代码也来实现实现:

TableCode = HandleTable->TableCode;

TableCode = (ULONG)TableCode & 0xFFFFFFFC;  //去掉低两位

#define  _SPECIAL_PURPOSE                8

//越过特殊用途8字节到第一个HandleTableEntry

HandleTableEntry = (PHANDLE_TABLE_ENTRY)((ULONG*)((UINT8*)TableCode + _SPECIAL_PURPOSE));

//去掉低3位掩码标志,转换为对象(体)指针

//EPROCESS地址

PVOID ObjectHeader = (PVOID)((ULONG)(HandleTableEntry->Object) & 0xFFFFFFF8);

Windbg继续开进,验证一下0x86aee798,这个地址的对象类型:

(在写这个程序代码的时候,就已经WINDBG查看了system进程的EPROCESS地址写死到了代码中,回头看看,正是这个0x86aee798!!!!)

一个Process对象,

Bingo!

代码无误,代接下来循环读取HandleTableEntry结构,打印出对象头和对象体:

NTSTATUS EnumTable0(PVOID TableCode)

{

    PHANDLE_TABLE_ENTRY HandleTableEntry = NULL;

    ULONG i = 0;

    //越过特殊用途8字节到第一个HandleTableEntry

    HandleTableEntry = (PHANDLE_TABLE_ENTRY)((ULONG*)((UINT8*)TableCode + _SPECIAL_PURPOSE));

    for (i = 0; i<_MAX; i++)

    {

         if (MmIsAddressValid((PVOID)HandleTableEntry))   //判断该虚拟内存是否合法

         {

             //去掉低3位掩码标志,转换为对象(体)指针

             //EPROCESS地址

             PVOID ObjectHeader = (PVOID)((ULONG)(HandleTableEntry->Object) & 0xFFFFFFF8);

             if (MmIsAddressValid(ObjectHeader))

             {

                  DbgPrint("ObjectHeader:%p\r\n",ObjectHeader);

                  PVOID ObjectBody = (PVOID)((UINT8*)ObjectHeader + _BODY_OFFSET_OBJECT_HEADER);

                  if (MmIsAddressValid(ObjectBody))   //这里应当判断对象是否合法

                  {

                      DbgPrint("Object:%p\r\n", ObjectBody);

                      __ObjectCount++;

                  }

             }

         }

         HandleTableEntry++;     //结构体指针++ 一加一个结构体

    }

    return STATUS_SUCCESS;

}

接下来回头谈谈_HANDLE_TABLE中的一个叫做NextHandleNeedingPool的成员:

这个成员描述了下次句柄表增长的时,起始的句柄值(别忘了句柄是以4为步长增长的),上面的分析我们知道系统有2个2级索引那么最多能描述512*2=1024个_HANDLE_ENTRY,也就是说最大能表示的句柄值为1024*4=4096=0x1000,因为是从0x00开始的,所以当前的索引表状态能够描述的最大句柄上限为0x1000,这个值也就是下次句柄表扩展的起始句柄值.

最后来验证一下通过PspCidTable和进程的PID找到进程对应的_EPROCESS:

以audiodg.exe作为检验对象。

它的PID为1048(十进制),应当位于第1048/4=262 个表项,我们每一个1级索引表能容纳512个表项,PID为1048应该在第1个1级索引的第262=0x106个表项(每个表项8Byte)

BINGO!

最后的最后总结一下流程:

(1)(WINDBG    )获取到PspCidTable的地址,根据Tablecode低2位判断句柄表的层数。

(2)遍历句柄表:只有一级句柄表才是_HANDLE_TABLE_ENTRY(8字节),二级和三级都是指针(4字节),每一个表都是1页(4KB)大小,

(3)获取到Object之后,可以通过ObjectHeader的TypeIndex看看是不是Process.

(4)HANDLE_TABLE_ENTRY 8Byte的前4Byte是一个Object对象,去掉低三位才是才是需要的_EPROCESS 对象的地址,_EPROCESS 对象偏移0x18处是相对于对象头的对象体。

源代码:

ScanProcessHandleTable.c
#include "ScanProcessHandleTable.h"

#define  _HANDLE_TABLE_OFFSET_EPROCESS  0x0f4
#define  _SPECIAL_PURPOSE                8
#define  _MAX                           511
#define  _BODY_OFFSET_OBJECT_HEADER     0x18

ULONG64  __ObjectCount = 0;

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
	UNREFERENCED_PARAMETER(RegisterPath);
	NTSTATUS Status = STATUS_SUCCESS;
	//system进程的eprocess地址
	PEPROCESS EProcess = (PEPROCESS)0x86aee798;
	PDEVICE_OBJECT  DeviceObject = NULL;

	DriverObject->DriverUnload = DriverUnload;

	SeScanProcessHandleTable(EProcess);

	return Status;
}

NTSTATUS SeScanProcessHandleTable(PEPROCESS EProcess)
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	PHANDLE_TABLE  HandleTable = NULL;
	PVOID  TableCode = NULL;
	ULONG  Flag = 0;
	if (EProcess==NULL)
	{
		return Status;
	}

	HandleTable = (PHANDLE_TABLE)(*((ULONG*)((UINT8*)EProcess + _HANDLE_TABLE_OFFSET_EPROCESS)));

	if (HandleTable==NULL)
	{
		return Status;
	}

	TableCode = HandleTable->TableCode;
	TableCode = (ULONG)TableCode & 0xFFFFFFFC;   //去掉低两位
    Flag = (ULONG)(HandleTable->TableCode) & 0x03;    //00 01 10 11
	switch (Flag)
	{
	case 0:
	{
		EnumTable0(TableCode);
		break;
	}
	case 1:
	{
		EnumTable1(TableCode);
		break;
	}
	case 2:
	{
		EnumTable2(TableCode);
		break;
	}
	case 3:
	{
		EnumTable3(TableCode);
		break;
	}
	}
}

NTSTATUS EnumTable0(PVOID TableCode)
{
	PHANDLE_TABLE_ENTRY HandleTableEntry = NULL;
	ULONG i = 0;
	//越过特殊用途8字节到第一个HandleTableEntry
	HandleTableEntry = (PHANDLE_TABLE_ENTRY)((ULONG*)((UINT8*)TableCode + _SPECIAL_PURPOSE));
	for (i = 0; i<_MAX; i++)
	{
		if (MmIsAddressValid((PVOID)HandleTableEntry))   //判断该虚拟内存是否合法
		{
			//去掉低3位掩码标志,转换为对象(体)指针
			//EPROCESS地址
			PVOID ObjectHeader = (PVOID)((ULONG)(HandleTableEntry->Object) & 0xFFFFFFF8);
			if (MmIsAddressValid(ObjectHeader))
			{
				DbgPrint("ObjectHeader:%p\r\n",ObjectHeader);
				PVOID ObjectBody = (PVOID)((UINT8*)ObjectHeader + _BODY_OFFSET_OBJECT_HEADER);
				if (MmIsAddressValid(ObjectBody))   //这里应当判断对象是否合法
				{
					DbgPrint("Object:%p\r\n", ObjectBody);
					__ObjectCount++;
				}
			}
		}
		HandleTableEntry++;     //结构体指针++ 一加一个结构体
	}
	return STATUS_SUCCESS;
}

NTSTATUS EnumTable1(PVOID TableCode)
{
	do
	{
		EnumTable0(*(ULONG*)TableCode);
		(UINT8*)TableCode += sizeof(ULONG);

	} while (*(ULONG*)TableCode != 0 && MmIsAddressValid(*(ULONG*)TableCode));

	return STATUS_SUCCESS;
}
NTSTATUS EnumTable2(PVOID TableCode)
{
	do
	{
		EnumTable1(*(ULONG*)TableCode);
		(UINT8*)TableCode += sizeof(ULONG);

	} while (*(ULONG*)TableCode != 0 && MmIsAddressValid(*(ULONG*)TableCode));

	return STATUS_SUCCESS;
}
NTSTATUS EnumTable3(PVOID TableCode)
{
	do
	{
		EnumTable2(*(ULONG*)TableCode);
		(UINT8*)TableCode += sizeof(ULONG);

	} while (*(ULONG*)TableCode != 0 && MmIsAddressValid(*(ULONG*)TableCode));

	return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	UNREFERENCED_PARAMETER(DriverObject);
	DbgPrint("DriverUnload()\r\n");
}

  

ScanProcessHandleTable.h
#pragma once
#include <ntifs.h>

typedef struct _HANDLE_TABLE
{
    PVOID TableCode;
    PEPROCESS QuotaProcess;
    HANDLE UniqueProcessId;
    ULONG HandleTableLock;
    LIST_ENTRY HandleTableList;
    ULONG HandleContentionEvent;
    PVOID DebugInfo;
    LONG  ExtraInfoPages;
    ULONG Flags;
    ULONG FirstFreeHandle;
    PVOID LastFreeHandleEntry;
    ULONG HandleCount;
    ULONG NextHandleNeedingPool;
    ULONG HandleCountHighWatermark;
} HANDLE_TABLE, *PHANDLE_TABLE;

typedef struct _HANDLE_TABLE_ENTRY
{
    union {
        PVOID Object;
        ULONG ObAttributes;
        PVOID InfoTable;
        PVOID Value;
    };
    union {
        union {
            ULONG GrantedAccess;
            struct {
                USHORT GrantedAccessIndex;
                USHORT CreatorBackTraceIndex;
            };
        };
        ULONG NextFreeTableEntry;
    };
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
NTSTATUS EnumTable0(PVOID TableCode);
NTSTATUS EnumTable1(PVOID TableCode);
NTSTATUS EnumTable2(PVOID TableCode);
NTSTATUS EnumTable3(PVOID TableCode);
NTSTATUS SeScanProcessHandleTable(PEPROCESS EProcess);
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
时间: 2024-10-26 23:14:32

扫描系统句柄表(WIN7 x86)(附录源码)的相关文章

最新dedecms网页游戏开服表发号网站源码模板

模板介绍 最新dedecms网页游戏开服表发号网站源码模板,让你更精确的把握游戏开服时间和战略技巧. 这套网页游戏模板是织梦二次开发后的一套网页源码,后台也是经过二次开发后,适合游戏网站的一些参数说明及添加信息. 模板文件: 404.htm            大气的404页面 article_fahao.htm  发号模板详细页 article_time.htm   发号时间详细页 footer_time.htm    发号时间底部模板 head_time.htm      发号时间头部模板

Java开源生鲜电商平台-RBAC系统权限的设计与架构(源码可下载)

Java开源生鲜电商平台-RBAC系统权限的设计与架构(源码可下载) 说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上分析实现的策略以及基于目前两种比较流行的权限设计思想来讨论关于权限系统的实现. 1.1.       技术策略 l         身份认证 在B/S的系统中,为识别用户身份,通常使用的技术策略为将用户的身份记录在Session中,也就是当用户登录时即获取用户的身份信息,并将其记录

仿快图系统自带图片浏览器应用源码项目

仿快图系统自带图片浏览器应用源码,最近在做一个微博i动态模块,需要查看他人相册照片或者微博内容图片等.看到QQ空间那个效果不错,尝试了不少方法来实现,均不是怎么理想.最初是想通过自定义GroupView和imageView来实现,结果在处理滑动事件和放大的图片拖曳不是很顺畅,自己也没深入解决,期望有高手实现了给分享下.后来看了网上的一些方法和帖子,尝试着拿别人的自定义包做一些修增自用.现在用gallery和imageVIew做的,感觉还不错.放上来最初的小demo,供大家参考分享. 源码下载:

【Android 系统开发】CyanogenMod 13.0 源码下载 编译 ROM 制作 ( 手机平台 : 小米4 | 编译平台 : Ubuntu 14.04 LTS 虚拟机)

作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/51592930 手机的两种模式 : 在下面有详细的图片示例; -- Recovery 模式 : 音量键增加 + 电源键, 长按上述组合键, 看到 "MI" 的 LOGO 后即进入 Recovery 模式; -- Fastboot 模式 : 音量键减小 + 电源键, 长按上述组合键, 看到 "FASTBOOT" 后, 即 进入 FA

android 消息系统Handler、MessageQueue、Looper源码学习

android消息系统 整体框架如图所示 在安卓的消息系统中,每个线程有一个Looper,Looper中有一个MessageQueue,Handler向这个队列中投递Message,Looper循环拿出Message再交由Handler处理.整体是一个生产者消费者模式,这四部分也就构成了android的消息系统. 先来看一个最简单的例子 //这段代码在某个Activity的onCreate中 Handler handler = new Handler(Looper.getMainLooper()

自动升级系统的设计与实现(源码)

对于PC桌面应用程序而言,自动升级功能往往是必不可少的.而自动升级可以作为一个独立的C/S系统来开发,这样,就可以在不同的桌面应用中进行复用.基于ESFramework的文件传送功能,我实现了一个可直接复用的自动升级系统OAUS,现在将其分享给大家.这篇文章将着重介绍OAUS的相关背景.使用方法,至于详细的实现细节,大家可以直接下载源码研究.如果了解了OAUS的使用,源码的理解就非常容易了.如果需要直接部署使用自动升级系统,那么,可下载文末的可执行程序压缩包. 一.OAUS的主要功能 目前主流的

linux系统程序安装(三)源码包安装程序

源码包安装是日常使用过程中最经常的安装方式,比如nagios套件.apche等重要软件都是源码包方式安装,源码包编译安装技术是运维技术中比较重要的部分. 一.源码包安装位置 运维时,最好把要跑的业务包放到/usr/local/src下,方便其他管理员管理代码. 转载一篇博客中的解析:(转载至https://itony.me/769.html) 二.源码安装方法 源码安装分为三个步骤:(1)./configure 定制程序的某些参数.检测系统是否具备程序运行的某些库.配置完成后,会生成mskefi

趣美优品系统开发|趣美优品源码搭建

趣美优品系统源码联系[佘经理135-6015-0274],趣美优品系统定制系统开发.趣美优品系统APP系统开发.趣美优品系统平台开发系统.趣美优品系统软件开发. 玩法介绍 趣美优品APP首创众多链上功能让用户粘性大幅度提升 签到任务 U豆登录就送,1秒签到即可获取,无任何门槛. 浏览任务 任务每天至商城界面浏览5个商品,每个商品12S,从而获得U豆奖励. 邀请奖励 每一个玩家都是趣美优品生态的建设者,你的付出,将从邀请机制上获得回报,你将获得你邀请人每一笔操作中获得分红 分享奖励 为了促进趣美优

Cocos2d-x源码阅读1 UI树(第一次系统而有成效的阅读源码的感悟)

之前我很少看源码,觉得枯燥又没有头绪.说实话现在看的也少,不过作为程序员要想成长,必须要突破自己的瓶颈吧. 也许我的天赋不在写代码这里,也许这是一个越走越难的路,也许这又是一个有金矿的浅坑,坚持下去就会挖到金矿. 然而没有那么多可以选择叻,试着去强大吧,即使自己不擅长,即使落后很多,即使,即使,即使,即使,.... http://cn.cocos2d-x.org/tutorial/show?id=2157 推荐大家去这里看这个视频,自己看源码可能没有头绪,跟着王老师的视频效果还是很好的. 这里是