欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内核对象HOOK

程序员文章站 2022-03-30 12:32:18
...

内核对象HOOK

内核对象的结构

首先我们来了解一下内核对象的基本结构,每一个内核对象都是由对象头和对象体组成,对象头都是一样的,对象体 不同的内核对象是不一样的。该结构体请参考内核结构体

//0x20 bytes (sizeof)
struct _OBJECT_HEADER
{
    LONG PointerCount;                                                      //0x0           //对象的指针计数
    union
    {
        LONG HandleCount;                                                   //0x4           //对象的引用计数
        VOID* NextToFree;                                                   //0x4
    };
    struct _EX_PUSH_LOCK Lock;                                              //0x8
    UCHAR TypeIndex;                                                        //0xc           //对象类型
    UCHAR TraceFlags;                                                       //0xd
    UCHAR InfoMask;                                                         //0xe
    UCHAR Flags;                                                            //0xf
    union
    {
        struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo;                //0x10
        VOID* QuotaBlockCharged;                                            //0x10
    };
    VOID* SecurityDescriptor;                                               //0x14
    struct _QUAD Body;                                                      //0x18           //对象ti
}; 

内核对象的获取

获取ObTypeIndexTable首地址, 地址在ObGetObjectType函数内部中出现,因此, 取函数首地址, 加上一定偏移就能得到该表的首地址.在win7 32系统下, 该表在函数中的0xF偏移处。

kd> u ObGetObjectType
	nt!ObGetObjectType:
	84291a72 8bff            mov     edi,edi
	84291a74 55              push    ebp
	84291a75 8bec            mov     ebp,esp
	84291a77 8b4508          mov     eax,dword ptr [ebp+8]
	84291a7a 0fb640f4        movzx   eax,byte ptr [eax-0Ch]
	84291a7e 8b0485c0801784  mov     eax,dword ptr nt!ObTypeIndexTable (841780c0)[eax*4]
	84291a85 5d              pop     ebp
	84291a86 c20400          ret     4

内核对象HOOK

     测试环境:
                          > windows7 32位
       HOOK步骤:
                          >找到内核对象原型数组
                          >遍历数组,得到指定类型的原型对象
                          >修改原型对象中的函数指针
       首先构造进程内核对象和函数原型。这里以Hook进程内核对象的OpenProcessDure函数为例,以打开记事本展开实验。
//内核对象
//0x88 bytes (sizeof)
struct _OBJECT_TYPE
{
    struct _LIST_ENTRY TypeList;                                            //0x0
    struct _UNICODE_STRING Name;                                            //0x8
    VOID* DefaultObject;                                                    //0x10
    UCHAR Index;                                                            //0x14
    ULONG TotalNumberOfObjects;                                             //0x18
    ULONG TotalNumberOfHandles;                                             //0x1c
    ULONG HighWaterNumberOfObjects;                                         //0x20
    ULONG HighWaterNumberOfHandles;                                         //0x24
    struct _OBJECT_TYPE_INITIALIZER TypeInfo;                               //0x28
    struct _EX_PUSH_LOCK TypeLock;                                          //0x78
    ULONG Key;                                                              //0x7c
    struct _LIST_ENTRY CallbackList;                                        //0x80
}; 

//0x50 bytes (sizeof)
//内核对象的操作函数结构体
struct _OBJECT_TYPE_INITIALIZER
{
    USHORT Length;                                                          //0x0
    union
    {
        UCHAR ObjectTypeFlags;                                              //0x2
        struct
        {
            UCHAR CaseInsensitive:1;                                        //0x2
            UCHAR UnnamedObjectsOnly:1;                                     //0x2
            UCHAR UseDefaultObject:1;                                       //0x2
            UCHAR SecurityRequired:1;                                       //0x2
            UCHAR MaintainHandleCount:1;                                    //0x2
            UCHAR MaintainTypeList:1;                                       //0x2
            UCHAR SupportsObjectCallbacks:1;                                //0x2
        };
    };
    ULONG ObjectTypeCode;                                                   //0x4
    ULONG InvalidAttributes;                                                //0x8
    struct _GENERIC_MAPPING GenericMapping;                                 //0xc
    ULONG ValidAccessMask;                                                  //0x1c
    ULONG RetainAccess;                                                     //0x20
    enum _POOL_TYPE PoolType;                                               //0x24
    ULONG DefaultPagedPoolCharge;                                           //0x28
    ULONG DefaultNonPagedPoolCharge;                                        //0x2c
    VOID (*DumpProcedure)(VOID* arg1, struct _OBJECT_DUMP_CONTROL* arg2);   //0x30
    LONG (*OpenProcedure)(enum _OB_OPEN_REASON arg1, CHAR arg2, struct _EPROCESS* arg3, VOID* arg4, ULONG* arg5, ULONG arg6); //0x34
    VOID (*CloseProcedure)(struct _EPROCESS* arg1, VOID* arg2, ULONG arg3, ULONG arg4); //0x38
    VOID (*DeleteProcedure)(VOID* arg1);                                    //0x3c
    LONG (*ParseProcedure)(VOID* arg1, VOID* arg2, struct _ACCESS_STATE* arg3, CHAR arg4, ULONG arg5, struct _UNICODE_STRING* arg6, struct _UNICODE_STRING* arg7, VOID* arg8, struct _SECURITY_QUALITY_OF_SERVICE* arg9, VOID** arg10); //0x40
    LONG (*SecurityProcedure)(VOID* arg1, enum _SECURITY_OPERATION_CODE arg2, ULONG* arg3, VOID* arg4, ULONG* arg5, VOID** arg6, enum _POOL_TYPE arg7, struct _GENERIC_MAPPING* arg8, CHAR arg9); //0x44
    LONG (*QueryNameProcedure)(VOID* arg1, UCHAR arg2, struct _OBJECT_NAME_INFORMATION* arg3, ULONG arg4, ULONG* arg5, CHAR arg6); //0x48
    UCHAR (*OkayToCloseProcedure)(struct _EPROCESS* arg1, VOID* arg2, VOID* arg3, CHAR arg4); //0x4c
}; 

//函数原型需用到该枚举变量
typedef enum _OB_OPEN_REASON { 
	ObCreateHandle,
	ObOpenHandle,
	ObDuplicateHandle,
	ObInheritHandle,
	ObMaxOpenReason
} OB_OPEN_REASON;


//OpenProcessDure函数原型
typedef NTSTATUS(*OB_OPEN_METHOD)(
	IN ULONG Unknown,
	IN OB_OPEN_REASON OpenReason,
	IN PEPROCESS Process OPTIONAL,
	IN PVOID Object,
	IN ACCESS_MASK GrantedAccess,
	IN ULONG HandleCount
	);

// 声明函数 获取内核对象表地址
PULONG _declspec(dllimport) ObGetObjectType(PVOID);

//用来保存系统进程对象地址
POBJECT_TYPE g_pFileObjTypeProcess;

//用来保存原来系统打开进程内核对象的函数
OB_OPEN_METHOD g_oldObjectParseProcess;

//定义自己的打开进程内核对象函数
NTSTATUS MyIopParseProcess(
	IN ULONG Unknown,
	IN OB_OPEN_REASON OpenReason,
	IN PEPROCESS Process OPTIONAL,
	IN PVOID Object,
	IN ACCESS_MASK GrantedAccess,
	IN ULONG HandleCount)
{
         //打开进程的名称保存的Object的偏移位0x16c位置,比较进程名
	if (
		RtlCompareMemory(((char*)Object + 0x16c), "notepad.exe", 12) == 12
		)
	{
               //当打开记事本程序时返回失败

		return STATUS_UNSUCCESSFUL;
	}
                //不是则调用系统打开进程的函数
	return ((OB_OPEN_METHOD)g_oldObjectParseProcess)(0, OpenReason, Process, Object, GrantedAccess, HandleCount);

}

//进程内核对象HOOK
NTSTATUS OnDeviceCtrlObjProcessHook(DEVICE_OBJECT* pDevice, IRP* pIrp)
{
	//获取typeIndexTable表的位置
	ULONG addr = ObGetObjectType;
	OBJECT_TYPE** typeIndexTable = (OBJECT_TYPE*)*(ULONG*)(addr + 0xF);
	//在表中找到文件内核对象
	ULONG i = 2;
	UNICODE_STRING fileTypeName;
	RtlInitUnicodeString(&fileTypeName, L"Process");
	POBJECT_TYPE fileObjType = NULL;

	// 遍历TypeIndex表,找到File对象的原型
	while (typeIndexTable[i])
	{
		// 判断类型名是否一致
		if (0 == RtlCompareUnicodeString(&typeIndexTable[i]->Name, &fileTypeName, TRUE))
		{
                        //保存内核对象的di'zhi
			g_pFileObjTypeProcess = typeIndexTable[i];
			break;
		}
		++i;
	}

	//替换函数地址
	if (g_pFileObjTypeProcess)
	{
		//保存老函数地址
		g_oldObjectParseProcess = (OB_OPEN_METHOD)g_pFileObjTypeProcess->TypeInfo.OpenProcedure;
		//替换新函数地址
		g_pFileObjTypeProcess->TypeInfo.OpenProcedure = (PVOID)MyIopParseProcess;
	}
}

//卸载进程内核对象HOOK
NTSTATUS OnDeviceCtrlUnloadProHook(DEVICE_OBJECT* pDevice, IRP* pIrp)
{
	if (g_pFileObjTypeProcess)
	{
		_asm sti; // 屏蔽中断
		// 将原始函数还原回去
		g_pFileObjTypeProcess->TypeInfo.OpenProcedure = g_oldObjectParseProcess;
		_asm cli; // 允许中断
	}
}

实验测试

      >新建文本文档,打开
      >Hook 进程内核对象
      >再次打开记事本失败

内核对象HOOK
内核对象HOOK

总结

此实验程序三环用MFC框架编写,利用设备IRP_MJ_DEVICE_CONTROL进行通讯,可以看成三环发送消息进内核,内核处理消息,这里就不多叙述了。
HOOK的关键在于找到内核对象原型数组,这里只介绍了32位win7环境下的方法。有兴趣的话可以查找其他方法。知道了以上过程,就可以对其他内核对象进行
HOOK了,方法大同小异。

相关标签: 内核