对象回调是目前绝大多数游戏保护用于保护游戏进程用的回调。 比如著名的 CF , 在 WIN64 系统上, TP 只有对象回调保护 CF 进程不被外挂修改其进程内容(这段话是 2013 年 12 月中旬研究 TP 得出的结论,不保证以后会不会变化), 如果使用 WIN64AST 摘除 TP 的两 个对象回调, TP 对 CF 进程的保护作用就会消失。
对象回调存储在对应对象结构体里, 简单来说,就是存储在 ObjectType. CallbackList 这个双向链表里。 但对象结构体在每个系统上都不一定相同。 比如 WIN7X64 的结构体如下:
ntdll!_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 指向的地址, 是一个结构体链表,它的定义如下:
typedef struct _OB_CALLBACK
{
LIST_ENTRY ListEntry;
ULONG64 Unknown;
ULONG64 ObHandle;
ULONG64 ObjTypeAddr;
ULONG64 PreCall;
ULONG64 PostCall;
} OB_CALLBACK, *POB_CALLBACK;
微软没有公开这个结构体的定义,这个结构体是我逆向出来的。但是至少在 WIN7 、 WIN8 和 WIN8.1 上通用。 知道了结构体的定义,枚举就方便了( WINDOWS 目前仅有进程对象回调和线程对象回调, 但就算以后有了其它回调,也是通用的):
ULONG EnumObCallbacks()
{
ULONG c = 0;
PLIST_ENTRY CurrEntry = NULL;
POB_CALLBACK pObCallback;
BOOLEAN IsTxCallback;
ULONG64 ObProcessCallbackListHead = *(ULONG64*)PsProcessType +
ObjectCallbackListOffset;
ULONG64 ObThreadCallbackListHead = *(ULONG64*)PsThreadType +
ObjectCallbackListOffset;
//
dprintf("ObProcessCallbackListHead: %p\n", ObProcessCallbackListHead);
CurrEntry = ((PLIST_ENTRY)ObProcessCallbackListHead)->Flink; //list_head 的数据是垃圾数据,忽略
do
{
pObCallback = (POB_CALLBACK)CurrEntry;
if (pObCallback->ObHandle != 0)
{
dprintf("ObHandle: %p\n", pObCallback->ObHandle);
dprintf("PreCall: %p\n", pObCallback->PreCall);
dprintf("PostCall: %p\n", pObCallback->PostCall);
c++;
}
CurrEntry = CurrEntry->Flink;
} while (CurrEntry != (PLIST_ENTRY)ObProcessCallbackListHead);
//
dprintf("ObThreadCallbackListHead: %p\n", ObThreadCallbackListHead);
CurrEntry = ((PLIST_ENTRY)ObThreadCallbackListHead)->Flink; //list_head 的数据是垃圾数据,忽略
do
{
pObCallback = (POB_CALLBACK)CurrEntry;
if (pObCallback->ObHandle != 0)
{
dprintf("ObHandle: %p\n", pObCallback->ObHandle);
dprintf("PreCall: %p\n", pObCallback->PreCall);
dprintf("PostCall: %p\n", pObCallback->PostCall);
c++;
}
CurrEntry = CurrEntry->Flink;
} while (CurrEntry != (PLIST_ENTRY)ObThreadCallbackListHead);
dprintf("ObCallback count: %ld\n", c);
return c;
}
代码运行的效果如下(干净的 WIN7X64 系统上是没有对象回调的,为了体现枚举效果)
对付对象回调, 方法还是老三套:
1. 用 ObUnRegisterCallbacks 传入 ObHandle 注销回调;
2. 把记录的回调函数地址改为自己的设置的空回调;
3. 给对方设置的回调函数地址写入 RET 。不过这次使用第三种方法要注意,必须先禁掉 PostCall , 再禁用 PreCall , 否则容易蓝屏, 不信自己试试。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python标准库
Doug Hellmann / 刘炽 / 机械工业出版社华章公司 / 2012-6-15 / 139.00元
本书由资深Python专家亲自执笔,Python语言的核心开发人员作序推荐,权威性毋庸置疑。 对于程序员而言,标准库与语言本身同样重要,它好比一个百宝箱,能为各种常见的任务提供完美的解决方案,所以本书是所有Python程序员都必备的工具书!本书以案例驱动的方式讲解了标准库中一百多个模块的使用方法(如何工作)和工作原理(为什么要这样工作),比标准库的官方文档更容易理解(一个简单的示例比一份手册......一起来看看 《Python标准库》 这本书的介绍吧!