绕TX驱动保护TesSafe.sys方法
2016-12-14 14:03
417 查看
前段时间看过 wanleidawa 兄弟发的帖子"【原创】搞定QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys",给我启发很大。
不过很多人可能对上面的内容不太能理解。我先简单说下他那里的意识,然后在描述下新的方法,来饶过现在的保护。
大家可以使用 反ROOTKIT程序22中文\Yas 来查看被HOOK的函数。这里还要插入一个小话题,就是关于驱动开发环境的搭建,当初也郁闷了我将近一天的时间。
http://bbs.pediy.com/showthread.php?t=48220 大家可以按照这里的介绍配置驱动环境。
使用Yas查看被HOOK 的函数基本没什么大的变化 大家可以参考 wanleidawa 兄弟发的帖子,关于反HOOK有很多种方法了,由于他那里没有讲,所以我说一下这部分的内容
(借用部分堕落天才的文章)。
SSDT即系统服务描述符表,它的结构如下(参考《Undocument Windows 2000 Secretes》第二章):
typedef struct _SYSTEM_SERVICE_TABLE
{
PVOID ServiceTableBase; //这个指向系统服务函数地址表
PULONG ServiceCounterTableBase;
ULONG NumberOfService; //服务函数的个数,NumberOfService*4 就是整个地址表的大小
ULONG ParamTableBase;
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
typedef struct _SERVICE_DESCRIPTOR_TABLE
SYSTEM_SERVICE_TABLE ntoskrnel; //ntoskrnl.exe的服务函数
SYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
SYSTEM_SERVICE_TABLE NotUsed1;
SYSTEM_SERVICE_TABLE NotUsed2;
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
内核中有两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出),一个是KeServieDescriptorTableShadow(没有导出)。两者的区别是,
KeServiceDescriptorTable仅有ntoskrnel一项,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的Native API的服务地址由KeServiceDescriptorTable分派,
gdi.dll/user.dll的内核API调用服务地址由KeServieDescriptorTableShadow分派。还有要清楚一点的是win32k.sys只有在GUI线程中才加载,一般情况下是不加载的,所以要Hook KeServieDescriptorTableShadow的话,一般是用一个GUI程序通过IoControlCode来触发(想当初不明白这点,蓝屏死机了N次都想不明白是怎么回事)。
2,SSDT HOOK
SSDT HOOK 的原理其实非常简单,我们先实际看看KeServiceDescriptorTable是什么样的。
lkd> dd KeServiceDescriptorTable
8055ab80 804e3d20 00000000 0000011c 804d9f48
8055ab90 00000000 00000000 00000000 00000000
8055aba0 00000000 00000000 00000000 00000000
8055abb0 00000000 00000000 00000000 00000000
在windbg.exe中我们就看得比较清楚,KeServiceDescriptorTable中就只有第一项有数据,其他都是0。
其中804e3d20就是KeServiceDescriptorTable.ntoskrnel.ServiceTableBase,服务函数个数为0x11c个。我们再看看804e3d20地址里是什么东西:
lkd> dd 804e3d20
804e3d20 80587691 805716ef 8057ab71 80581b5c
804e3d30 80599ff7 80637b80 80639d05 80639d4e
804e3d40 8057741c 8064855b 80637347 80599539
804e3d50 8062f4ec 8057a98c 8059155e 8062661f
如上,80587691 805716ef 8057ab71 80581b5c 这些就是系统服务函数的地址了。比如当我们在ring3调用OpenProcess时,进入sysenter的ID是0x7A(XP
SP2),然后系统查KeServiceDescriptorTable,大概是这样KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08,然后804E3F08 ->8057559e 这个就是
OpenProcess系统服务函数所在,我们再跟踪看看:
lkd> u 8057559e nt!NtOpenProcess:
8057559e 68c4000000 push 0C4h
805755a3 6860b54e80 push offset nt!ObReferenceObjectByPointer+0x127 (804eb560)
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
805755ad 33f6 xor esi,esi
原来8057559e就是NtOpenProcess函数所在的起始地址。
嗯,如果我们把8057559e改为指向我们函数的地址呢?比如 MyNtOpenProcess,那么系统就会直接调用MyNtOpenProcess,而不是原来的NtOpenProcess了。这就是SSDT HOOK原理所在。
3, ring0 inline hook
ring0 inline hook 跟ring3的没什么区别了,如果硬说有的话,那么就是ring3发生什么差错的话程序会挂掉,ring0发生什么差错的话系统就挂掉,所以一定要很小心。
inline hook的基本思想就是在目标函数中JMP到自己的监视函数,做一些判断然后再JMP回去。一般都是修改函数头,不过再其他地方JMP也是可以的。下面我们来点实际的吧:
lkd> u nt!NtOpenProcess
nt!NtOpenProcess:
8057559e e95d6f4271 jmp f199c500
805755a3 e93f953978 jmp f890eae7
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
...
同时打开“冰刃”跟“Rootkit Unhooker”我们就能在NtOpenProcess函数头看到这样的“奇观”,第一个jmp是“冰刃”的,第二个jmp是“Rootkit Unhooker”的。他们这样是防止被恶意程序通过TerminateProcess关闭。当然“冰刃”还Hook了NtTerminateProcess等函数。
Undocumented Windows 2000 Secrets 这本书我有中文电子版,需要的可以找我。
早期一般的驱动这样恢复基本都可以,甚至一些防病毒的主动防御等等都类似这样,不过现在的保护已经不象以前了,问题在于他多了一个监视机制。
比如NP,多个发送验证包。如果对NP做了手脚,包发送不出去,那游戏也别想玩。 韩国NP好象就没这样BT,(听做韩国脱机朋友说的)
回到问题上,进行反HOOK。
我这里拿比较经典的 NtOpenProcess这个函数(这个函数比较重要,研究的人也就多一些),发现其在NtOpenProcess函数调用ObOpenObjectByPointer的地方下了个跳转,跳转到TX游戏自身的代码里面,从而使其他程序无法通过此函数获取TX游戏的进程句柄而达到保护的目的。
顺便说句这个函数:OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。 常用的还有 GetWindowThreadProcessId 这个函数获得指定线程的标识符,此线程创建了指定的窗口,并且随机的产生了这个标识符,要求必须知道窗口标题窗口类(SSDTShadow HOOK 保护的函数保护了这部分)。
参考wanleidawa的文章描述:
NtOpenProcess + 0x2xx Call ObOpenObjectByPointer处
NtOpenThread + 0x1xx Call ObOpenObjectByPointer处
NtOpenProcess + 0x2xx NtOpenThread + 0x1xx 在TesSafe加载前,先保存ObOpenObjectByPointer的地址(或者用MmGetSystemRoutineAddress获取),然后我们自己写一段代码,实现Call
ObOpenObjectByPointer头N个字节(随便自己)以及Call ObOpenObjectByPointer,然后再jmp到Call ObOpenObjectByPointer后面的代码地址,如:
push eax
push dword ptr [ebp-38h]
push dword ptr [ebp-24h]
Call ObOpenObjectByPointer
jmp xxx
然后在Call ObOpenObjectByPointer前面N个字节处jmp到我们自制的代码,这样的话就算TesSafe把Call ObOpenObjectByPointer改成Call到自己的函数,对我们也没有作用了。
注意:直接还原代码的话,势必会蓝屏。因为TesSafe对这个地址有代码效验
首先使用经典常规的恢复Inline Hook方法,可惜TX驱动一检测到Hook被恢复便马上重启电脑。代码可以参考下这里http://bbs.pediy.com/showthread.php?t=77467&highlight=Inline+Hook
到底如何才能饶过保护又不被发现呢,大家可以参考下堕落天才的文章。
对付ring0 inline hook的基本思路是这样的,自己写一个替换的内核函数,以NtOpenProcess为例,就是MyNtOpenProcess。然后修改SSDT表,让系统服务进入自己的函数MyNtOpenProcess。而MyNtOpenProcess要做的事就是,实现NtOpenProcess前10字节指令,然后再JMP到原来的NtOpenProcess的十字节后。这样NtOpenProcess函数头写的JMP都失效了,在ring3直接调用OpenProcess再也毫无影响。
***********************************************************************************************************
就这么多了,或许有人说,没必要那么复杂,直接恢复NtOpenProcess不就行了吗?对于象“冰刃”“Rookit Unhooker”这些“善良”之辈的话是没问题的,但是象NP这些“穷凶极恶”之流的话,它会不断检测NtOpenProcess是不是已经被写回去,是的话,嘿嘿,机器马上重启。这也是这种方法的一点点妙用。
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
现在我们把思路总结一下,首先在游戏启动前先把NtOpenProcess整个函数的代码保存下来,再SSDT Hook,把地址指向我的函数。但是发现TX的驱动还有个保护机制 ,每隔一段时间检测系统是否有调用NtOpenProcess函数。
转载自:http://blog.csdn.net/ccx_john/article/details/17959393
前段时间看过 wanleidawa 兄弟发的帖子"【原创】搞定QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys",给我启发很大。
不过很多人可能对上面的内容不太能理解。我先简单说下他那里的意识,然后在描述下新的方法,来饶过现在的保护。
大家可以使用 反ROOTKIT程序22中文\Yas 来查看被HOOK的函数。这里还要插入一个小话题,就是关于驱动开发环境的搭建,当初也郁闷了我将近一天的时间。
http://bbs.pediy.com/showthread.php?t=48220 大家可以按照这里的介绍配置驱动环境。
使用Yas查看被HOOK 的函数基本没什么大的变化 大家可以参考 wanleidawa 兄弟发的帖子,关于反HOOK有很多种方法了,由于他那里没有讲,所以我说一下这部分的内容
(借用部分堕落天才的文章)。
SSDT即系统服务描述符表,它的结构如下(参考《Undocument Windows 2000 Secretes》第二章):
typedef struct _SYSTEM_SERVICE_TABLE
{
PVOID ServiceTableBase; //这个指向系统服务函数地址表
PULONG ServiceCounterTableBase;
ULONG NumberOfService; //服务函数的个数,NumberOfService*4 就是整个地址表的大小
ULONG ParamTableBase;
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
typedef struct _SERVICE_DESCRIPTOR_TABLE
SYSTEM_SERVICE_TABLE ntoskrnel; //ntoskrnl.exe的服务函数
SYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
SYSTEM_SERVICE_TABLE NotUsed1;
SYSTEM_SERVICE_TABLE NotUsed2;
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
内核中有两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出),一个是KeServieDescriptorTableShadow(没有导出)。两者的区别是,
KeServiceDescriptorTable仅有ntoskrnel一项,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的Native API的服务地址由KeServiceDescriptorTable分派,
gdi.dll/user.dll的内核API调用服务地址由KeServieDescriptorTableShadow分派。还有要清楚一点的是win32k.sys只有在GUI线程中才加载,一般情况下是不加载的,所以要Hook KeServieDescriptorTableShadow的话,一般是用一个GUI程序通过IoControlCode来触发(想当初不明白这点,蓝屏死机了N次都想不明白是怎么回事)。
2,SSDT HOOK
SSDT HOOK 的原理其实非常简单,我们先实际看看KeServiceDescriptorTable是什么样的。
lkd> dd KeServiceDescriptorTable
8055ab80 804e3d20 00000000 0000011c 804d9f48
8055ab90 00000000 00000000 00000000 00000000
8055aba0 00000000 00000000 00000000 00000000
8055abb0 00000000 00000000 00000000 00000000
在windbg.exe中我们就看得比较清楚,KeServiceDescriptorTable中就只有第一项有数据,其他都是0。
其中804e3d20就是KeServiceDescriptorTable.ntoskrnel.ServiceTableBase,服务函数个数为0x11c个。我们再看看804e3d20地址里是什么东西:
lkd> dd 804e3d20
804e3d20 80587691 805716ef 8057ab71 80581b5c
804e3d30 80599ff7 80637b80 80639d05 80639d4e
804e3d40 8057741c 8064855b 80637347 80599539
804e3d50 8062f4ec 8057a98c 8059155e 8062661f
如上,80587691 805716ef 8057ab71 80581b5c 这些就是系统服务函数的地址了。比如当我们在ring3调用OpenProcess时,进入sysenter的ID是0x7A(XP
SP2),然后系统查KeServiceDescriptorTable,大概是这样KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08,然后804E3F08 ->8057559e 这个就是
OpenProcess系统服务函数所在,我们再跟踪看看:
lkd> u 8057559e nt!NtOpenProcess:
8057559e 68c4000000 push 0C4h
805755a3 6860b54e80 push offset nt!ObReferenceObjectByPointer+0x127 (804eb560)
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
805755ad 33f6 xor esi,esi
原来8057559e就是NtOpenProcess函数所在的起始地址。
嗯,如果我们把8057559e改为指向我们函数的地址呢?比如 MyNtOpenProcess,那么系统就会直接调用MyNtOpenProcess,而不是原来的NtOpenProcess了。这就是SSDT HOOK原理所在。
3, ring0 inline hook
ring0 inline hook 跟ring3的没什么区别了,如果硬说有的话,那么就是ring3发生什么差错的话程序会挂掉,ring0发生什么差错的话系统就挂掉,所以一定要很小心。
inline hook的基本思想就是在目标函数中JMP到自己的监视函数,做一些判断然后再JMP回去。一般都是修改函数头,不过再其他地方JMP也是可以的。下面我们来点实际的吧:
lkd> u nt!NtOpenProcess
nt!NtOpenProcess:
8057559e e95d6f4271 jmp f199c500
805755a3 e93f953978 jmp f890eae7
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
...
同时打开“冰刃”跟“Rootkit Unhooker”我们就能在NtOpenProcess函数头看到这样的“奇观”,第一个jmp是“冰刃”的,第二个jmp是“Rootkit Unhooker”的。他们这样是防止被恶意程序通过TerminateProcess关闭。当然“冰刃”还Hook了NtTerminateProcess等函数。
Undocumented Windows 2000 Secrets 这本书我有中文电子版,需要的可以找我。
早期一般的驱动这样恢复基本都可以,甚至一些防病毒的主动防御等等都类似这样,不过现在的保护已经不象以前了,问题在于他多了一个监视机制。
比如NP,多个发送验证包。如果对NP做了手脚,包发送不出去,那游戏也别想玩。 韩国NP好象就没这样BT,(听做韩国脱机朋友说的)
回到问题上,进行反HOOK。
我这里拿比较经典的 NtOpenProcess这个函数(这个函数比较重要,研究的人也就多一些),发现其在NtOpenProcess函数调用ObOpenObjectByPointer的地方下了个跳转,跳转到TX游戏自身的代码里面,从而使其他程序无法通过此函数获取TX游戏的进程句柄而达到保护的目的。
顺便说句这个函数:OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。 常用的还有 GetWindowThreadProcessId 这个函数获得指定线程的标识符,此线程创建了指定的窗口,并且随机的产生了这个标识符,要求必须知道窗口标题窗口类(SSDTShadow HOOK 保护的函数保护了这部分)。
参考wanleidawa的文章描述:
NtOpenProcess + 0x2xx Call ObOpenObjectByPointer处
NtOpenThread + 0x1xx Call ObOpenObjectByPointer处
NtOpenProcess + 0x2xx NtOpenThread + 0x1xx 在TesSafe加载前,先保存ObOpenObjectByPointer的地址(或者用MmGetSystemRoutineAddress获取),然后我们自己写一段代码,实现Call
ObOpenObjectByPointer头N个字节(随便自己)以及Call ObOpenObjectByPointer,然后再jmp到Call ObOpenObjectByPointer后面的代码地址,如:
push eax
push dword ptr [ebp-38h]
push dword ptr [ebp-24h]
Call ObOpenObjectByPointer
jmp xxx
然后在Call ObOpenObjectByPointer前面N个字节处jmp到我们自制的代码,这样的话就算TesSafe把Call ObOpenObjectByPointer改成Call到自己的函数,对我们也没有作用了。
注意:直接还原代码的话,势必会蓝屏。因为TesSafe对这个地址有代码效验
首先使用经典常规的恢复Inline Hook方法,可惜TX驱动一检测到Hook被恢复便马上重启电脑。代码可以参考下这里http://bbs.pediy.com/showthread.php?t=77467&highlight=Inline+Hook
到底如何才能饶过保护又不被发现呢,大家可以参考下堕落天才的文章。
对付ring0 inline hook的基本思路是这样的,自己写一个替换的内核函数,以NtOpenProcess为例,就是MyNtOpenProcess。然后修改SSDT表,让系统服务进入自己的函数MyNtOpenProcess。而MyNtOpenProcess要做的事就是,实现NtOpenProcess前10字节指令,然后再JMP到原来的NtOpenProcess的十字节后。这样NtOpenProcess函数头写的JMP都失效了,在ring3直接调用OpenProcess再也毫无影响。
***********************************************************************************************************
#include<ntddk.h> typedef struct _SERVICE_DESCRIPTOR_TABLE { PVOID ServiceTableBase; PULONG ServiceCounterTableBase; ULONG NumberOfService; ULONG ParamTableBase; }SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; //由于KeServiceDescriptorTable只有一项,这里就简单点了 extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;//KeServiceDescriptorTable为导出函数 } ///////////////////////////////////// VOID Hook(); VOID Unhook(); VOID OnUnload(IN PDRIVER_OBJECT DriverObject); ////////////////////////////////////// ULONG JmpAddress;//跳转到NtOpenProcess里的地址 ULONG OldServiceAddress;//原来NtOpenProcess的服务地址 ////////////////////////////////////// __declspec(naked) NTSTATUS __stdcall MyNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) { DbgPrint("NtOpenProcess() called"); __asm{ push 0C4h push 804eb560h //共十个字节 jmp [JmpAddress] } } /////////////////////////////////////////////////// NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath) { DriverObject->DriverUnload = OnUnload; DbgPrint("Unhooker load"); Hook(); return STATUS_SUCCESS; } ///////////////////////////////////////////////////// VOID OnUnload(IN PDRIVER_OBJECT DriverObject) { DbgPrint("Unhooker unload!"); Unhook(); } ///////////////////////////////////////////////////// VOID Hook() { ULONG Address; Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//0x7A为NtOpenProcess服务ID DbgPrint("Address:0x%08X",Address); OldServiceAddress = *(ULONG*)Address;//保存原来NtOpenProcess的地址 DbgPrint("OldServiceAddress:0x%08X",OldServiceAddress); DbgPrint("MyNtOpenProcess:0x%08X",MyNtOpenProcess); JmpAddress = (ULONG)NtOpenProcess + 10; //跳转到NtOpenProcess函数头+10的地方,这样在其前面写的JMP都失效了 DbgPrint("JmpAddress:0x%08X",JmpAddress); __asm{//去掉内存保护 cli mov eax,cr0 and eax,not 10000h mov cr0,eax } *((ULONG*)Address) = (ULONG)MyNtOpenProcess;//HOOK SSDT __asm{//恢复内存保护 mov eax,cr0 or eax,10000h mov cr0,eax sti } } ////////////////////////////////////////////////////// VOID Unhook() { ULONG Address; Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//查找SSDT __asm{ cli mov eax,cr0 and eax,not 10000h mov cr0,eax } *((ULONG*)Address) = (ULONG)OldServiceAddress;//还原SSDT __asm{ mov eax,cr0 or eax,10000h mov cr0,eax sti } DbgPrint("Unhook"); }
就这么多了,或许有人说,没必要那么复杂,直接恢复NtOpenProcess不就行了吗?对于象“冰刃”“Rookit Unhooker”这些“善良”之辈的话是没问题的,但是象NP这些“穷凶极恶”之流的话,它会不断检测NtOpenProcess是不是已经被写回去,是的话,嘿嘿,机器马上重启。这也是这种方法的一点点妙用。
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
现在我们把思路总结一下,首先在游戏启动前先把NtOpenProcess整个函数的代码保存下来,再SSDT Hook,把地址指向我的函数。但是发现TX的驱动还有个保护机制 ,每隔一段时间检测系统是否有调用NtOpenProcess函数。
转载自:http://blog.csdn.net/ccx_john/article/details/17959393
相关文章推荐
- 最新绕过TX驱动保护TesSafe.sys方法
- 过游戏保护之 过TX驱动保护TesSafe.sys方法(现在可以用)3
- QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys
- win10系统腾讯驱动文件tessafe.sys导致蓝屏的修复方法
- 搞定QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys
- win10升级邮政银行网银键盘保护驱动 peckp.sys 阻止升级的解决方法
- 搞定QQ游戏系列驱动保护TesSafe.sys
- 过NP 系列之一---搞定QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys
- 一个 映象劫持+dll注入+驱动保护 小毒的清理方法
- 针对近期的驱动病毒usb8028,usb8028x,以及一个随机的保护驱动的解决方法
- 过TX DXF驱动保护的文源代码
- 从exe驱动安装文件中提取sys和inf等驱动文件的方法
- 从exe驱动安装文件中提取sys和inf等驱动文件的方法
- 针对近期的驱动病毒usb8028,usb8028x,以及一个随机的保护驱动的解决方法
- 关于asp.net Ajax v1.0.61025版(即1.0 rc) 错误:'sys'未定义解决方法.
- 鼠标驱动之-sys节点-input子系统
- Red Hat安装Broadcom网卡驱动方法
- 声卡驱动安装正确却不能发声的解决方法
- 介绍一种很棒的wince驱动调试方法——在wince应用程序中直接访问硬件(作者:gooogleman)
- 判定表驱动分析方法