枚举和移除对象回调

对象回调是目前绝大多数游戏保护用于保护游戏进程用的回调。
对象回调存储在对应对象结构体里, 简单来说,就是存储在 ObjectType. CallbackList 这个双向链表里。

1: kd> dt _object_type
nt!_OBJECT_TYPE
+0x000 TypeList         : _LIST_ENTRY
+0x010 Name             : _UNICODE_STRING
+0x020 DefaultObject    : Ptr64 Void
+0x028 Index            : UChar
+0x02c TotalNumberOfObjects : Uint4B
+0x030 TotalNumberOfHandles : Uint4B
+0x034 HighWaterNumberOfObjects : Uint4B
+0x038 HighWaterNumberOfHandles : Uint4B
+0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
+0x0b0 TypeLock         : _EX_PUSH_LOCK
+0x0b8 Key              : Uint4B
+0x0c0 CallbackList     : _LIST_ENTRY      这个里面的回调函数

按理来说,知道了回调存储的地址,枚举应该就很简单了。 但是只有一个链表能知道什
么? 对象回调至少有三个关键信息: PreCall 函数地址, PostCall 函数地址, 回调句柄。 这些
信息藏在哪里呢?当年我对此感到百思不得其解。 后来经过研究, 发现秘密就藏在
CallbackList 的第二项以及之后。 换句话说,在 ListHead->Flink 以及之后大有乾坤。
Object.CallbackList->FLink 指向的地址, 是一个结构体链表,_OB_CALLBACK

对象目录是操作系统组织命名对象的一个数据结构, 是由根节点对象ObpRootDirectoryObject维
护的一张哈希数组,而每个数组都会存在一条链表,在链表中可能又存在另一个哈希数组。

引入了对象目录,那么我们清楚了命名对象是通过对象目录管理的,查找也是通过这个数据结
构统一管理的,那么无名对象又是如何查找的呢

_object_header:对象头
_object:对象=对象头地址+0x30

对象结构
OBJECT_HEADER_QUOTA_INFO
OBJECT_HEADER_HANDLE_INFO
OBJECT_HEADER_NAME_INFO
OBJECT_HEADER
Object Body

对象头
nt!_OBJECT_HEADER
+0x000 PointerCount     : Int8B
+0x008 HandleCount      : Int8B
+0x008 NextToFree       : Ptr64 Void
+0x010 Lock             : _EX_PUSH_LOCK
+0x018 TypeIndex        : UChar         // 这个变量表示当前对象头在全局变量ObTypeIndexTable中的索引值,这个变量是一个数组首地址,存储所有的_Object_Type结构 可以查看这个变量 dq nt!ObTypeIndexTable
                                        // win10中这个变量需要计算得到,看winsubsystem pdf
+0x019 TraceFlags       : UChar
+0x01a InfoMask         : UChar
+0x01b Flags            : UChar
+0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : Ptr64 Void
+0x028 SecurityDescriptor : Ptr64 Void
+0x030 Body             : _QUAD

#include <ntddk.h>

typedef struct _OB_CALLBACK
{
	LIST_ENTRY	ListEntry;
	ULONG64		Unknown;
	ULONG64		ObHandle;
	ULONG64		ObjTypeAddr;
	ULONG64		PreCall;
	ULONG64		PostCall;
} OB_CALLBACK, *POB_CALLBACK;

ULONG NtBuildNumber = 0;
ULONG ObjectCallbackListOffset = 0;

BOOLEAN GetVersionAndHardCode()
{
	BOOLEAN b = FALSE;
	RTL_OSVERSIONINFOW	osi;
	osi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
	RtlFillMemory(&osi, sizeof(RTL_OSVERSIONINFOW), 0);
	RtlGetVersion(&osi);
	NtBuildNumber = osi.dwBuildNumber;
	DbgPrint("NtBuildNumber: %ld\n", NtBuildNumber);
	switch (NtBuildNumber)
	{
	case 7600:
	case 7601:  // 虚拟机是这个 
	{
		ObjectCallbackListOffset = 0xC0;
		b = TRUE;
		break;
	}
	case 9200:
	{
		ObjectCallbackListOffset = 0xC8;	//OBJECT_TYPE.CallbackList
		b = TRUE;
		break;
	}
	case 9600:
	{
		ObjectCallbackListOffset = 0xC8;	//OBJECT_TYPE.CallbackList
		b = TRUE;
		break;
	}
	default:
		break;
	}
	return b;
}

ULONG EnumObCallback()
{
	ULONG c = 0;
	PLIST_ENTRY CurrEntry = NULL;
	POB_CALLBACK pObCallback;
	BOOLEAN IsTxCallback;
	GetVersionAndHardCode();
	ULONG64 ObProcessCallbackListHead = *(ULONG64*)PsProcessType + ObjectCallbackListOffset;
	ULONG64 ObThreadCallbackListHead = *(ULONG64*)PsThreadType + ObjectCallbackListOffset;
	//
	DbgPrint("ObProcessCallbackListHead: %p\n", ObProcessCallbackListHead);
	CurrEntry = ((PLIST_ENTRY)ObProcessCallbackListHead)->Flink;	//list_head的数据是垃圾数据,忽略
	do
	{
		pObCallback = (POB_CALLBACK)CurrEntry;
		if (pObCallback->ObHandle != 0)
		{
			DbgPrint("ObHandle: %p\n", pObCallback->ObHandle);
			DbgPrint("PreCall: %p\n", pObCallback->PreCall);
			DbgPrint("PostCall: %p\n", pObCallback->PostCall);
			c++;
		}
		CurrEntry = CurrEntry->Flink;
	} while (CurrEntry != (PLIST_ENTRY)ObProcessCallbackListHead);
	//
	DbgPrint("ObThreadCallbackListHead: %p\n", ObThreadCallbackListHead);
	CurrEntry = ((PLIST_ENTRY)ObThreadCallbackListHead)->Flink;	//list_head的数据是垃圾数据,忽略
	do
	{
		pObCallback = (POB_CALLBACK)CurrEntry;
		if (pObCallback->ObHandle != 0)
		{
			DbgPrint("ObHandle: %p\n", pObCallback->ObHandle);
			DbgPrint("PreCall: %p\n", pObCallback->PreCall);
			DbgPrint("PostCall: %p\n", pObCallback->PostCall);
			c++;
		}
		CurrEntry = CurrEntry->Flink;
	} while (CurrEntry != (PLIST_ENTRY)ObThreadCallbackListHead);
	DbgPrint("ObCallback count: %u\n", c);
	return c;
}


/*

对付对象回调方法
1.用ObUnRegisterCallbacks传入ObHandle注销回调
2.把记录的回调函数地址改为自己的设置的空回调
3.给对方设置的回调函数地址写入ret,必须先禁掉PostCall,再禁用PreCall,否则容易蓝屏。

*/

VOID DisableObcallbacks(PVOID Address)
{
	KIRQL irql;
	CHAR patchCode[] = "\x33\xC0\xC3";	//xor eax,eax + ret
	if (!Address)
		return;
	if (MmIsAddressValid(Address))
	{
		irql = WPOFFx64();
		memcpy(Address, patchCode, 3);
		WPONx64(irql);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值