首页
社区
课程
招聘
[旧帖] [原创]TP驱动保护-浅谈-总结 0.00雪花
发表于: 2012-5-15 18:25 4380

[旧帖] [原创]TP驱动保护-浅谈-总结 0.00雪花

2012-5-15 18:25
4380

今天写一篇关于某款游戏的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期)

收藏
免费 6
支持
分享
最新回复 (12)
雪    币: 297
活跃值: (265)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
2
支持一个。  楼主加油
2012-5-15 19:45
0
雪    币: 85
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
顶一个,拿到邀请码就羡煞人也
2012-5-16 08:59
0
雪    币: 75
活跃值: (39)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
估计不会给吧。。。
2012-5-18 15:55
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
谢谢楼主,这文章真是好
2012-5-19 06:25
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢你多多写好文章指导新手
2012-5-19 14:26
0
雪    币: 8
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
就算把,也能调试了,断点等都过了了。

再被混淆的代码中,又要怎么做能得到有用的数据呢?
2012-5-19 20:38
0
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这能过才见鬼了,port123都是变量。每次更新都变,你搞虾米啊。现在的人啊发贴都是乱来
2012-5-28 00:06
0
雪    币: 219
活跃值: (783)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
9
。。。。。这样子就精华了
     没源码    至少给个DOC
2012-5-29 14:05
0
雪    币: 48
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
现在的人咋都这个鸟样?
你指责别人的不对,那就你把不对的理由说一下,列出你的理由,而不是随便一句话了事了。
到底是你在误导,还是别人在误导,该相信谁呢
2012-8-10 17:28
0
雪    币: 508
活跃值: (202)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
11
我想这位朋友说的是~~~~~~~~
清0的偏移是会变的~~~~~~~~~
而且也没有给出偏移,或给出找到偏移的方法
有点虎头蛇尾的感觉不过可以过掉保护应该不错了
2012-8-18 13:36
0
雪    币: 508
活跃值: (202)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
12
兄弟的破文和这个朋友的有8成相同~~~~~~~~~~~~
http://bbs.pediy.com/showthread.php?t=126802&highlight=dnf
2012-8-18 14:32
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
还是V大的AGP牛叉,一个大招TP就没了。。。
2012-8-31 16:14
0
游客
登录 | 注册 方可回帖
返回
//