今天写一篇关于某款游戏的TP保护学习总结(分析谈不上)吧,搞这驱动大概弄了2个星期,中间有点事情等于断断续续的加起来搞了2个星期,因为TP、HP、GPK等等,当前比较流行的游戏保护,都有很多大牛已经研究透了,并且非常潇洒的公布出来了。所有对我们的学习有了很大的帮助,在这里向他们致敬!你们永远活在我们的心中。。。
类似的文章也已经有很多了,都写的非常好,超越他们是很难的了,之所以写这篇文章,主要有几点,一来我最近没有在搞驱动保护,算是个复习吧,所以想写一篇文章总结梳理下驱动那点事儿,二来在驱动中我还是有很多地方有些不明白,还有些问题,写出来大家想想,帮我想想,也帮自己想想,指不定哪天也遇到了可以迅速的解决。
好了,进入正题:
如果想学习驱动保护或者驱动方面的技术,你首先要补充下自己的知识,否则你会一头雾水,不知去向。建议大家如果是想做windows驱动方面的 就学习下Win32汇编,因为Windows底层不是完全开源的,有些东西必须反汇编才能看到,然后就是Windows底层实现原理,也就是了解下Windows是怎么协调工作的其中就包括我们要用到的SSDT,如果你在学习windows底层的话 这个你就会明白的,还有就是编程语言了,C、C++、delphi、汇编 根据自己擅长的用。语言只是工具而已。当然了,调试工具你还是要会用的。WinDBG、syser、等。根据自己需要。
上面说了一些相关的东西,现在我们来说说TP保护。TP保护我就不介绍了,各种百度。。。。。
TP需要处理的内容:
SSDT HOOK:
NtOpenProcess
NtOpenThread
NtReadVirtualMemory
NtWriteVirtualMemory
KiAttachPRocess
清零和监控:
两处清零和一处监控
因为不是D*F 所有清零不是4处
注意在检查调试的时候一定要等游戏全部进入到账号登陆的时候,因为TP会运行两次,第一次是假的,所以我们必须至少等游戏进入账号登陆那个界面才行。
首先解决前两个因为 NtOpenProcess和NtOpenThread的处理是一样的,但后面三个是不一样的,这样个有一点点的难度,其实都一样。。。。
如果,大家不知道TP都现在这些函数的那些部分,那你就那TP没运行直接的函数体和运行后的函数体进行文本对比就行了。
push dword ptr [ebp-34h]
push dword ptr [nt!PsProcessType (8055b258)]
push ebx
call nt!ObOpenObjectByName (805b1f7a)
通过对比你会发现就是这里出来问题。。上面的代码是没有运行TP之前的。
TP的做法是:把call nt!ObOpenObjectByName (805b1f7a) 替换成他自己的函数名字。也就是说,TP来控制什么时候调用ObOpenObjectByName函数,所以TP就机会对我们做一些处理了,具体TP在调用ObOpenObjectByName函数之前都做了什么,我也没有具体的读代码(主要是技术不到家。。)但大牛们都说是和游戏客户端进行通信用的。比如:游戏调用OpenProcess的时候他需要给某个全局变量赋值。然后再调用ObOpenObjectByName函数让系统正常的走下去,如果游戏端在检测这个变的时候发现没有变动,或者值不对,那么TP就会认为,没有按照他的步骤走,就报异常了。。。这是打个比方,主要是让大家明白,我们不能直接把他改回去,所以,我们需要在进入他的CALL之前先进入我们的CALL,我们先判断下,是谁在调用这个OpenProcess 如果是游戏进程的话,我们就让他进入他CALL(也就是让它去修改那个全局变量,保证游戏的验证)如果是我们的调试器的话就直接调用ObOpenObjectByName函数就可以了。、。。。这是解决OpenProcess、OpenThread HOOK的思路,以前都已经有人写过了,我今天只是加了一点我自己的理解,希新手朋友们能够理解这点。下面发下具体实现:
代码可以分为两部分看:一部分是在OpenProcess函数中插入我们的CALL ,另一部分是到我们CALL里面的处理代码。
修改OpenProcess:
__asm
{
//我们写入OpenProcess的被HOOK地址
mov ebx,p_MyOpenProcessHook//我们自己的函数地址
mov byte ptr [ebx+0],0xe9 //jmp
lea eax,My_NtOpenPrcoess //通过公式计算
sub eax,p_MyOpenProcessHook
sub eax,5
mov [ebx+1],eax
mov [ebx+5],0x90 //nop
}
__asm
{
//我们写入OpenThread的被HOOK地址
mov ebx,p_MyOpenThreadHook//我们自己的函数地址
mov byte ptr [ebx+0],0xe9 //jmp
lea eax,My_NtOpenThread //通过公式计算
sub eax,p_MyOpenThreadHook
sub eax,5
mov [ebx+1],eax
mov [ebx+5],0x90 //nop
}
第二部分:我们的函数实现:
//我们自己的:NtOpenPrcoess 处理函数
__declspec(naked) VOID My_NtOpenPrcoess()
{
//获得调用者的EPROCESS
processEPROCESS = IoGetCurrentProcess();
RtlInitAnsiString(&p_str1,SG_EXE);
//获得调用者的EPROCESS
RtlInitAnsiString(&p_str2,(PCSZ)((ULONG)processEPROCESS+0x174));
if (RtlCompareString(&p_str1,&p_str2,TRUE) == 0)
{
__asm
{
push dword ptr [ebp-38h]
push dword ptr [ebp-24h]
mov eax,p_TpHookAddress // CALL B2165421 ;
jmp eax
}
}else
{
__asm
{
push dword ptr [ebp-38h]
push dword ptr [ebp-24h]
push p_ReturnAddress //8BF8 MOV EDI,EDI 0x80572907
mov eax,ObOpenObjectByPointerAddress
jmp eax
}
}
}
//我们自己的NtOpenThread 实现函数
__declspec(naked)VOID My_NtOpenThread()
{
//获得调用者的EPROCESS
processEPROCESS = IoGetCurrentProcess();
RtlInitAnsiString(&p_str1,SG_EXE);
//获得调用者的EPROCESS
RtlInitAnsiString(&p_str2,(PCSZ)((ULONG)processEPROCESS+0x174));
if (RtlCompareString(&p_str1,&p_str2,TRUE) == 0)
{
__asm
{
push dword ptr [ebp-34h]
push dword ptr [ebp-20h]
mov eax,p_TpHookAddress_Thread
jmp eax
}
}else
{
__asm
{
push dword ptr [ebp-34h]
push dword ptr [ebp-20h]
push p_ReturnAddress_Thread
mov eax,ObOpenObjectByPointerAddress
jmp eax
}
}
}
下面开始处理剩下的三个函数
1.NtReadVirtualMemory
2.NtWriteVirtualMemory
3.KiAttachPRocess
这三个函数非常好处理,他们都是函数头部有个JMP跳转,处理的方法直接对照着没有HOOK之前用汇编写回去就可以了。。
直接上代码:
/**
功能:将原来没有被HOOK前的参数或者是被HOOK的部分还原
并且调用原来的函数地址+被HOOK的前面一部分
这样就 实现了跳过HOOK
**/
__declspec(naked) NTSTATUS __stdcall MyKiAttachProcess(HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToKiAttach,
PULONG NumberOfBytesReaded)
{
//跳过去
__asm
{
mov edi,edi
push ebp
mov ebp,esp
push ebx
mov ebx,dword ptr [ebp+0Ch]
mov eax,Old_KiAttach_Address //KiAttachPRocess函数的地址
add eax,7
jmp eax
}
}
/**
当调用NtReadVirtualMemory函数的时候 跳转到这个函数中
这个函数会模式出 NtReadVirtualMemory原来的参数并跳转
JmpAddress:原来没有HOOK前的NtReadVirtualMemory函数+7个的地址
因为 前7个没TP给修改了 所有不能用了!
**/
__declspec(naked) NTSTATUS __stdcall MyNtReadVirtualMemory(HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToRead,
PULONG NumberOfBytesReaded)
{
//跳过去
__asm
{
push 0x1C
push 804F8E28h //共十个字节
jmp [JmpAddress]
}
}
/**
当调用NtWriteVirtualMemory函数的时候 跳转到这个函数中
这个函数会模式出 NtWriteVirtualMemory
JmpAddress:原来没有HOOK前的NtWriteVirtualMemory函数+7个的地址
因为 前7个没TP给修改了 所有不能用了!
**/
__declspec(naked) NTSTATUS __stdcall MyNtWriteVirtualMemory(HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToWrite,
PULONG NumberOfBytesReaded)
{
//跳过去
__asm
{
push 0x1c
push 804F8E40h //共十个字节
jmp [JmpAddress1]
}
}
这三个函数没有什么好说的 不懂的朋友可以看看其他大牛写的类似文章。
接下来就是清零和监控的处理了。。。。。
我先简单的说下思路吧,首先要搞明白清零是指 EPROCESS结构中的DebugPort 它是调试端口,传送一些调试信息的,所以,如果TP不断的把这个属性的值给清空的话,那么我们是不是就没有办法获取调试信息了?那我们就应该找到是哪里对DebugPort做了手脚,说白了就是哪里对DebugPort作了读和写的操作,我们直接在DebugPort的地址下读和写的断点,我用的是syser 然后让游戏运行一会的时候syser就断下来,那就证明这个地方对DebugPort做了手脚,我们可以找到函数的头部 NOP掉就可以了。当然不会只有一个函数在对DebugPort读和写 ,有多少NOP多少。
注意在NOP 之前 还有个东西要处理。。那就是有一个专门的方法在不断的检测着 对DebugPort操作的函数,所以我们要先解决它 再NOP上面的函数。找监控的方法和上面的一样 只不过把下断的地址 换成 你要NOP的函数的头地址就可以了。。记住先 NOP监控再来NOP清零。。。。
代码:
NTSTATUS My_Recovery_Debugport()
{
ULONG ModuleAddress,ModuleSize;
NTSTATUS stats;
stats = MyEnumKernelModule("\\??\\c:\\windows\\system32\\tessafe.sys",&ModuleAddress,&ModuleSize);
//if (stats == FAILED_TO_OBTAIN_FUNCTION_ADDRESSES)
//{
//return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
//}
KdPrint("Address:%0X Size:%d \n",ModuleAddress,ModuleSize);
port1Address=ModuleAddress+port1;
port2Address=ModuleAddress+port2;
port3Address=ModuleAddress+port3;
DbgPrint("清零的地址1111111:%x",(ULONG)port1Address);
DbgPrint("清零的地址2222222:%x",(ULONG)port2Address);
DbgPrint("清零的地址3333333:%x",(ULONG)port3Address);
WPOFF();
__asm
{
mov ax,0c390h
mov edx,port3Address
mov WORD ptr [edx],ax
}
__asm
{
mov ax,0c390h
mov edx,port2Address
mov WORD ptr [edx],ax
}
__asm
{
mov ax,0c390h
mov edx,port1Address
mov WORD ptr [edx],ax
}
WPON();
}
好了,事情发展到现在,已经基本收尾了,当然了 还有很多其他的要处理,根据自己的需要 比如:双击调试、硬件断点、等等。。今天说的主要是思路,不是代码。需要代码的朋友看大牛贴。。。。
经过我的测试 CE OD 都可以出数据。。但是会报非法,至今困惑啊。。。。
不过换别的内存工具就没事,可能TP还有别的定时器之类的东东在检测OD和CE吧
如果有知道的大牛请赐教。。。。
最后 希望有志同道合的朋友一起讨论学习。由于有的论坛不能发扣扣 所以站内联系。
没有什么技术含量的帖子,大牛莫拍砖。。。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)