发表于 2016-8-16 11:08

申请会员ID:钱木头【申请通过】

1、申 请 I D:钱木头
2、个人邮箱:460874@qq.com
3、原创技术文章:http://bbs.pediy.com/showthread.php?t=126802

负责人您好,本屌归隐多年。无ID 希望通过打赏几个分,下载点东西。
来和大家一起学习进步。




======================================================================================
// 我是帖子
标 题: 【原创】散谈游戏保护那点事~就从_TP开始入手吧
作 者: crazyearl
时 间: 2010-12-20,02:37:22
链 接: http://bbs.pediy.com/showthread.php?t=126802

声明:本文只为研究技术,请所有童鞋切勿使用本文之方法做下那天理难容罪恶不舍之坏事。
         既是研究游戏保护,那么总要有一个研究对象。本文就以TMD_TP这款游戏保护为例进行分析讲解。请勿对号入座,如有雷同之处。纯属反汇编引擎之错误,不关我的事!
       转载请注明出处
      关键字:DNF 驱动保护
      鉴于最近很多同学找上门来求解这那问题,反正这东西又不是绝密档案,放在我手里大半个月了,还不如放出来让大家一起进步算了。另外都是取之看雪还之看雪罢了。
索性我也就公布一个全套的方案。绝无其他意思,所以还请同道中人嘴下留情。切勿背地使坏!http://bbs.pediy.com/images/smilies/124.gifhttp://bbs.pediy.com/images/smilies/124.gifhttp://bbs.pediy.com/images/smilies/124.gifhttp://bbs.pediy.com/images/smilies/124.gif

      在正式开篇之前我要感谢看雪ID:十年寒窗 在我最困惑的时候,他给予了最大的帮助!另外还有一位和我同岁的神秘人物也给予了不小的帮助,感谢你们。

      废话了半天,正式开始吧。
tmd_TP也就是国内比较流行的游戏D_N*F的游戏保护。
它在ring0层一共HOOK了几个地方和一些其他的工作。来达到保护的目的
下面是简报:
引用:
NtOpenThread    //防止调试器在它体内创建线程
NtOpenProcess   //防止OD等在进程列表看到它
KiAttachProcess   //防止其他软件附加它
NtReadVirtualMemory//防止别人读取它的内存
NtWriteVirtualMemory//防止别人在它的内存里面乱写乱画
KDCOM.dll:KdReceivePacket//这两个是COM串口的接受和发送数据
KDCOM.dll:KdSendPacket      //主要用来方式别人双机调试

使用了KdDisableDebugger来禁用双机调试
代码:
.text:010025F0               jz      short loc_1002622.text:010025F2               call    sub_10022A4.text:010025F7               call    ds:KdDisableDebugger.text:010025FD               push    offset byte_10022EC.text:01002602               push    esi.text:01002603               push    offset byte_10022DC.text:01002608               push    edi.text:01002609               push    dword_100CF24

并对debugport进行了疯狂的清零操作
甚至还包括EPROCESS+70\+74\+78等几处位置
http://bbs.pediy.com/attachment.php?attachmentid=52306&thumb=1&d=1292779733

处理的手段通常都是向64端口写入FE导致计算机被重启
代码:
.text:01001665               mov   al, 0FEh.text:01001667               out   64h, al         ; AT Keyboard controller 8042..text:01001667                                       ; Resend the last transmission.text:01001669               popa.text:0100166A               retn

下面简单看下他关键的几个HOOK:
KiAttachProcess   
http://bbs.pediy.com/attachment.php?attachmentid=52300&thumb=1&d=1292779221

NtReadVirtualMemory
http://bbs.pediy.com/attachment.php?attachmentid=52301&thumb=1&d=1292779221

NtWriteVirtualMemory
http://bbs.pediy.com/attachment.php?attachmentid=52302&thumb=1&d=1292779221

NtOpenThread
http://bbs.pediy.com/attachment.php?attachmentid=52304&thumb=1&d=1292779315

NtOpenProcess
http://bbs.pediy.com/attachment.php?attachmentid=52305&thumb=1&d=1292779315

引用:
其中,前3个直接恢复即可。
第4个有监视,直接恢复即刻重启
第5个和ring3有通信,直接恢复1分钟内SX非法模块


根据上面的分析,下面给出相应的解决方案
1.直接恢复 第1、2、3处HOOK
2.绕过4、5处HOOK
3.将debugport清零的内核线程干掉
4.恢复硬件断点
但是要有一个先后的逻辑顺序
因为内核有一个线程负责监视几个地方,必须要先干掉它。
但是这个内容我写在了处理debugport清零的一起,也就是第3步。所以大家在照搬源码的时候
注意代码执行次序

先从简单的工作讲起,恢复1、2、3处的HOOK
KiAttachProcess的处理
代码:
////////////////////////////////////////////////////////////////////////名称:Nakd_KiAttachProcess//功能:My_RecoveryHook_KiAttachProcess的中继函数//参数://返回://////////////////////////////////////////////////////////////////////static NAKED VOIDNakd_KiAttachProcess(){__asm{    mov   edi,edi    push    ebp    mov   ebp,esp    push    ebx    push    esi    mov    eax,KiAttachProcessAddress//注意这个是全局变量 BYTE*    add    eax,7    jmp    eax}}////////////////////////////////////////////////////////////////////////名称:RecoveryHook_KiAttachProcess//功能:解除游戏保护对_KiAttachProcess函数的HOOK(DNF)//参数://返回:状态//////////////////////////////////////////////////////////////////////NTSTATUS My_RecoveryHook_KiAttachProcess(){BYTE    *KeAttachProcessAddress = NULL;//KeAttachProcess函数地址BYTE    *p;BYTE    MovEaxAddress= {0xB8,0,0,0,0};//BYTE    JmpEax      = {0xff,0xe0};KIRQL    Irql;//特征码BYTESignature1 = 0x56,//p-1      Signature2 = 0x57,//p-2      Signature3 = 0x5F,//p-3      Signature4 = 0x5E,//p+5      Signature5 = 0xE8;//p第一个字节   //获得KeAttachProcess地址,然后通过特征码找到//KiAttachProcess的地址KeAttachProcessAddress = (BYTE*)MyGetFunAddress(L"KeAttachProcess");if (KeAttachProcessAddress == NULL){    KdPrint(("KeAttachProcess地址获取失败\n"));    returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;}//将p指向KeAttachProcess函数开始处p = KeAttachProcessAddress;while (1){    if ((*(p-1) == Signature1) &&      (*(p-2) == Signature2) &&      (*(p+5) == Signature3) &&      (*(p+6) == Signature4) &&      (*p    == Signature5))    {      //定位成功后取地址      KiAttachProcessAddress = *(PULONG)(p+1)+(ULONG)(p+5);      break;    }   //推动指针    p++;}   //计算中继函数地址*(ULONG *)(MovEaxAddress+1)=(ULONG)Nakd_KiAttachProcess;   WPOFF();//清除CR0//提升IRQL中断级Irql=KeRaiseIrqlToDpcLevel();//写入RtlCopyMemory(KiAttachProcessAddress,MovEaxAddress,5);RtlCopyMemory(KiAttachProcessAddress+5,JmpEax,2);//恢复IrqlKeLowerIrql(Irql);WPON();    //恢复CR0   returnSTATUS_SUCCESS;}

NtReadVirtualMemory和
NtWriteVirtualMemory的处理
注意这里,我对他们俩开头的第2句PUSH的处理
我直接写入了push 0x78563412
大家可以根据自己的地址来硬编码一次。
或者干脆这样使用
代码:
////////////////////////////////////////////////////////////////////////名称:My_RecoveryHook_NtReadAndWriteMemory//功能:解除游戏保护对NtReadVirtualMemory和//      NtWriteVirtualMemory的HOOK//参数://返回://////////////////////////////////////////////////////////////////////NTSTATUS My_RecoveryHook_NtReadAndWriteMemory(){BYTEPush1Ch= {0x6a,0x1c};//0~2字节BYTEPushAdd= {0x68,0x12,0x34,0x56,0x78};//NtReadVirtualMemory[物理机]//BYTEPushAdd2= {0x68,0xf0,0x6f,0x4f,0x80};//NtWriteVirtualMemory[物理机]KIRQLIrql;BYTE*NtReadVirtualMemoryAddress    = NULL;//NtReadVirtualMemory的地址BYTE*NtWriteVirtualMemoryAddress= NULL;//NtWriteVirtualMemory的地址   //从SSDT表中获取NtReadVirtualMemory函数地址NtReadVirtualMemoryAddress = (BYTE*)myGetCurrentAddress(0xBA);if (NtReadVirtualMemoryAddress == NULL){    KdPrint(("NtReadVirtualMemory函数地址获取失败! \n"));    returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;}//从SSDT表中获取NtWriteVirtualMemory函数地址NtWriteVirtualMemoryAddress = (BYTE*)myGetCurrentAddress(0x115);if (NtWriteVirtualMemoryAddress == NULL){    KdPrint(("NtWriteVirtualMemory函数地址获取失败! \n"));    returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;}   WPOFF();//清除CR0//提升IRQL中断级Irql=KeRaiseIrqlToDpcLevel();//写入RtlCopyMemory(NtReadVirtualMemoryAddress,Push1Ch,2);RtlCopyMemory(NtReadVirtualMemoryAddress+2,PushAdd,5);   RtlCopyMemory(NtWriteVirtualMemoryAddress,Push1Ch,2);RtlCopyMemory(NtWriteVirtualMemoryAddress+2,PushAdd,5);//恢复IrqlKeLowerIrql(Irql);WPON();    //恢复CR0   returnSTATUS_SUCCESS;}

好了,下面来处理
NtOpenProcess和
NtOpenThread
这两个函数的处理上不能太鲁莽了。
手法要风骚一点细腻一点了
介于篇幅的原因,我只贴出来前者的处理方法,后者雷同
细微之处大家自行修改。我总不能真的给你方法又给你工具。眼看着自己变成教唆犯
代码:
//NtOpenProcess用到的全局变量[为了方便堆栈平衡的处理使用全局变量]PEPROCESSprocessEPROCESS = NULL;//保存访问者的EPROCESSANSI_STRINGp_str1,p_str2;      //保存进程名称BYTE    *ObOpenObjectByPointerAddress= NULL; //ObOpenObjectByPointer的地址BYTE    *p_TpHookAddress = NULL;      //TP的HOOK函数地址BYTE    *p_ReturnAddress = NULL;      //返回到的地址BYTE    *p_MyHookAddress = NULL;      //我们的HOOK函数在哪写入#define DNF_EXE"DNF.exe"//要检索的进程名////////////////////////////////////////////////////////////////////////名称:Nakd_NtOpenProcess//功能:My_RecoveryHook_NtOpenProcess的中继函数//参数://返回://////////////////////////////////////////////////////////////////////static NAKED VOIDNakd_NtOpenProcess(){//获得调用者的EPROCESSprocessEPROCESS = IoGetCurrentProcess();//将调用者的进程名保存到str1中RtlInitAnsiString(&p_str1,(ULONG)processEPROCESS+0x174);//将我们要比对的进程名放入str2RtlInitAnsiString(&p_str2,DNF_EXE);if (RtlCompareString(&p_str1,&p_str2,TRUE) == 0){    //说明是DNF进程访问了这里    __asm    {      push    dword ptr       push    dword ptr       pushp_ReturnAddress      mov    eax,p_TpHookAddress      jmp    eax    }}else{    __asm    {      push    dword ptr       push    dword ptr       pushp_ReturnAddress      mov    eax,ObOpenObjectByPointerAddress      jmp    eax    }}} ////////////////////////////////////////////////////////////////////////名称:My_RecoveryHook_NtOpenProcess//功能:解除游戏保护对NtOpenProcess的HOOK//参数://返回:状态//////////////////////////////////////////////////////////////////////NTSTATUS My_RecoveryHook_NtOpenProcess(){BYTE    *NtOpenProcessAddress      = NULL;//NtOpenProcess的地址BYTE    *p = NULL;      //临时TOP5CODE*top5code = NULL;//保存5字节内容BYTE    JmpAddress = {0xE9,0,0,0,0,0x90};KIRQL    Irql;   //获取NtOpenProcess的地址    NtOpenProcessAddress = (BYTE*)MyGetFunAddress(L"NtOpenProcess");    if (NtOpenProcessAddress == NULL)    {      KdPrint(("NtOpenProcess地址获取失败\n"));      returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;    }    //获取ObOpenObjectByPointer的地址    ObOpenObjectByPointerAddress = (BYTE*)MyGetFunAddress(L"ObOpenObjectByPointer");    if (ObOpenObjectByPointerAddress == NULL)    {      KdPrint(("ObOpenObjectByPointer地址获取失败\n"));      returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;    }   //将p指向NtOpenProcess函数开始处    p = NtOpenProcessAddress;    //用一个无限循环来判断给定的特征码来确定被HOOK位置    while (1)    {      if ((*(p-7)    == 0x50) &&         (*(p-0xE)== 0x56) &&      (*(p+0xd)== 0x50) &&      (*(p+0x16)== 0x3b) &&      (*(p+0x17)== 0xce) &&      (*p      == 0xE8) &&      (*(p+5)    == 0x8b) &&      (*(p+6)    == 0xf8))      {      KdPrint(("%0X \n",(ULONG)p));      break;      }      //推动指针向前走      p++;    }   //将top5code指向 p 的当前处    //用以取出 call [地址] 这5字节里面的地址    top5code = (TOP5CODE*)p;    p_TpHookAddress = (BYTE*)((ULONG)p+5+top5code->address);   //找到我们写入自定义函数的地址    p_MyHookAddress = p-6;    //保存调用ObOpenObjectByPointer函数以后的返回地址    p_ReturnAddress = p+5;   //将一条JMP Nakd_NtOpenProcess写入到数组中    *(ULONG *)(JmpAddress+1)=(ULONG)Nakd_NtOpenProcess - ((ULONG)p_MyHookAddress+5);   WPOFF();//清除CR0    //提升IRQL中断级    Irql=KeRaiseIrqlToDpcLevel();    //写入    RtlCopyMemory(p_MyHookAddress,JmpAddress,6);    //恢复Irql    KeLowerIrql(Irql);    WPON();    //恢复CR0    returnSTATUS_SUCCESS;}
处理之后:
http://bbs.pediy.com/attachment.php?attachmentid=52307&thumb=1&d=1292781564
简而言之其原理就是,任何人调用了NtOpenProcess的时候会先进入
Nakd_NtOpenProcess函数,我们判断。如果是游戏进程访问的话,就有可能是验证之类的
我们转到它自己的函数里面。让它保持与ring3层的通信。否则的话,嘿嘿……

接下来是第3步处理debugport清零的这块了。
我想绝大多数人关心的都是这里了
网络上能搜多到的办法几乎都失效了
有办法的人又不肯放出来,急眼了就自己想了个土办法
虽然不那么时尚。但是绝对的奏效。
由于代码凌乱不堪,简单说下其原理。
我们定位内核模块TxxxSxxx.sys的首地址
然后根据特征码遍历整个模块找到我们需要的地方,然后干掉他们。
那么我们又如何能够通过人工的判断出来到底是哪里在作怪呢
利用syser或Start SoftICE对EPROCESS+BC处设置断点。就可以一层一层的追溯上去了
到底如何用他们,我想大家自己多花点时间在看雪和GOOGLE或者BAIDU上面是不会吃亏的。
由于ZwQuerySystemInformation函数的使用非常繁琐。而且篇幅有限。所以我只给出关键代码,至于这个函数如何使用。大家可以自己在搜索引擎找“枚举内核模块”
代码:
////////////////////////////////////////////////////////////////////////名称:MyEnumKernelModule//功能:枚举内核模块//参数:str:内核模块名称//      moduleadd:该模块地址[传出]//      modulesie:该模块大小[传出]//返回://////////////////////////////////////////////////////////////////////NTSTATUS MyEnumKernelModule(IN CHAR* str,OUT ULONG *moduleadd,OUT ULONG *modulesie){NTSTATUS status = STATUS_SUCCESS;ULONG   n       = 0;ULONG   i       = 0;PSYSTEM_MODULE_INFORMATION_ENTRY   module = NULL;PVOID   pbuftmp = NULL;ANSI_STRING    ModuleName1,ModuleName2;BOOLEANtlgstst= FALSE;//如果找到了指定模块则设置为TRUE   //利用11号功能枚举内核模块status = ZwQuerySystemInformation(11, &n, 0, &n);   //申请内存pbuftmp = ExAllocatePool(NonPagedPool, n);   //再次执行,将枚举结果放到指定的内存区域status = ZwQuerySystemInformation(11, pbuftmp, n, NULL);   module = (PSYSTEM_MODULE_INFORMATION_ENTRY)((PULONG )pbuftmp + 1 );   //初始化字符串   RtlInitAnsiString(&ModuleName1,str);//n       = *((PULONG)pbuftmp );for ( i = 0; i < n; i++ ){    RtlInitAnsiString(&ModuleName2,&module.ImageName);    //DbgPrint("%d\t0x%08X 0x%08X %s\n",module.LoadOrderIndex,module.Base,module.Size,module.ImageName);   if (RtlCompareString(&ModuleName1,&ModuleName2,TRUE) == 0)    {      DbgPrint("MyEnumKernelModule:%s:%0X \n",ModuleName2.Buffer,module.Base);      *moduleadd= module.Base;      *modulesie= module.Size;      tlgstst = TRUE;      break;    }}ExFreePool(pbuftmp);if tlgstst == FALSE){    returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;}return status;} ////////////////////////////////////////////////////////////////////////名称:My_Recovery_Debugport//功能:恢复游戏对debugport的清零操作//参数://返回://////////////////////////////////////////////////////////////////////NTSTATUS My_Recovery_Debugport(){NTSTATUS stats;BYTE*sd1 = NULL,*sd2 = NULL,*pd = NULL;ULONGModuleSize,ModuleAddress,i,number = 0;BYTE*p;KIRQLIrql;BYTEC390 = {0xc3,0x90};   //获取指定的内核模块地址和字节数stats = MyEnumKernelModule("\\??\\c:\\windows\\system32\\tessafe.sys",&ModuleAddress,&ModuleSize);if (stats == FAILED_TO_OBTAIN_FUNCTION_ADDRESSES){    returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;}KdPrint(("Address:%0X Sie:%d \n",ModuleAddress,ModuleSize));//特征码/*sd1特征p-1:18 p-2:87 p-3:DB p-4:33 p-5:07   p-6:03 p:33 p+1:C0 p+7:3B p+8:D8    sd2特征p-1:07 p-2:87 p-3:c0 p-4:33 p+14:89p+15:1c p+16:38*/   //将P指向内核模块开始处p = (BYTE*)ModuleAddress + 20;for (i = 0; i < ModuleSize - 20; i++,p++){    //sd1    if ((*(p-1) == 0x18) &&      (*(p-2) == 0x87) &&      (*(p-3) == 0xDB) &&      (*(p-4) == 0x33) &&      (*(p-5) == 0x07) &&      (*(p-6) == 0x03) &&      (*p    == 0x33) &&      (*(p+1) == 0xC0) &&      (*(p+7) == 0x3B) &&      (*(p+8) == 0xD8) )    {      KdPrint(("--SD1 -- %0X \n",(ULONG)p));      sd1 = p;      number+=1;//记录已经获取一个特征    }    //sd2    if ((*(p-1) == 0x07) &&      (*(p-2) == 0x87) &&      (*(p-3) == 0xC0) &&      (*(p-4) == 0x33) &&      (*(p+14)== 0x89) &&      (*(p+15)== 0x1C) &&      (*(p+16)== 0x38) &&      (*p    == 0xA1))    {      KdPrint(("--SD2 -- %0X \n",(ULONG)p));      sd2 = p;      number+=1;//记录已经获取一个特征    }    //pd    if ((*(p-2) == 0xE3) &&      (*(p-3) == 0xC1) &&      (*(p-7) == 0xF3) &&      (*(p-8) == 0x33) &&      (*(p-10)== 0xEB) &&      (*(p-11)== 0xC1) &&      (*(p+1) == 0xF3) &&      (*(p+2) == 0x42) &&      (*(p+3) == 0x3B) &&      (*(p+4) == 0xD1) &&      (*p    == 0x33))    {      KdPrint(("--PD -- %0X \n",(ULONG)p));      pd = p;      number+=1;//记录已经获取一个特征    }    if (number >= 3)    {      KdPrint(("特征 %d ---退出\n",number));      break;    }}   //首先干掉监视函数while (1){    if ((*(pd-1) == 0xcc) && (*(pd-2) == 0xcc))    {      KdPrint(("pd首地址:%0X \n",(ULONG)pd));      WPOFF();//清除CR0      //提升IRQL中断级      Irql=KeRaiseIrqlToDpcLevel();      //写入      RtlCopyMemory(pd,C390,2);      //恢复Irql      KeLowerIrql(Irql);      WPON();    //恢复CR0      break;    }    pd--;}//干掉2个SDwhile (1){    if ((*(sd1-1) == 0xcc) && (*(sd1-2) == 0xcc))    {      KdPrint(("sd1首地址:%0X \n",(ULONG)sd1));      WPOFF();//清除CR0      //提升IRQL中断级      Irql=KeRaiseIrqlToDpcLevel();      //写入      RtlCopyMemory(sd1,C390,2);      //恢复Irql      KeLowerIrql(Irql);      WPON();    //恢复CR0      break;    }    sd1--;}while (1){    if ((*(sd2-1) == 0xcc) && (*(sd2-2) == 0xcc))    {      KdPrint(("sd2首地址:%0X \n",(ULONG)sd2));      WPOFF();//清除CR0      //提升IRQL中断级      Irql=KeRaiseIrqlToDpcLevel();      //写入      RtlCopyMemory(sd2,C390,2);      //恢复Irql      KeLowerIrql(Irql);      WPON();    //恢复CR0      break;    }    sd2--;}   returnSTATUS_SUCCESS;}

最后,处理一下硬件断点就可以了
这里我们使用到了SSDT HOOK
分别HOOK了 SSDT 表中索引为 0xD5和0x55的函数。由于这里比较简单
我想10个人有9个人懂得SSDT HOOK的。所以直接给出源码,不做原理分析了
代码:
//处理硬件断点时ULONG    uNtSetContextThreadAddress;ULONG    uNtGetContextThreadAddress;ULONG    TenNtSetContextThread,       TenNtGetContextThread;////////////////////////////////////////////////////////////////////////名称:_MyNtGetThreadContext//功能:两个SSDT HOOK伪造函数的中继函数//参数://返回://////////////////////////////////////////////////////////////////////static NAKED NTSTATUS Nakd_NtGetThreadContext(HANDLE hThread, PCONTEXT pContext){__asm{    jmp    dword ptr}} static NAKED NTSTATUS Nakd_NtSetThreadContext(HANDLE hThread, PCONTEXT pContext){__asm{    jmp    dword ptr}}////////////////////////////////////////////////////////////////////////名称:MyNtGetThreadContext && MyNtSetThreadContext//功能:NtGetThreadContext与NtSetThreadContext函数被SSDT HOOK的伪造函数//参数://返回://////////////////////////////////////////////////////////////////////NTSTATUS MyNtGetThreadContext(HANDLE hThread, PCONTEXT pContext){if ( _stricmp((const char*)PsGetProcessImageFileName(PsGetCurrentProcess()),DNF_EXE) ){    return Nakd_NtGetThreadContext(hThread, pContext);}return STATUS_UNSUCCESSFUL;}NTSTATUS MyNtSetThreadContext(HANDLE hThread, PCONTEXT pContext){if ( _stricmp((const char*)PsGetProcessImageFileName(PsGetCurrentProcess()),DNF_EXE) ){    return Nakd_NtSetThreadContext(hThread, pContext);   }//DbgPrint("Dr7:%08X\n", pContext->Dr7);if ( pContext->Dr7 == 0x101 ){    return Nakd_NtSetThreadContext(hThread, pContext);   }return STATUS_UNSUCCESSFUL; } ////////////////////////////////////////////////////////////////////////名称:My_Recovery_HardwareBreakpoint//功能:通过对set与get进行SSDT HOOK来恢复硬件断点//参数://返回://////////////////////////////////////////////////////////////////////NTSTATUS My_Recovery_HardwareBreakpoint(){KIRQL    Irql;//获取地址uNtSetContextThreadAddress = (ULONG)KeServiceDescriptorTable->ServiceTableBase+0xD5 * 4;uNtGetContextThreadAddress = (ULONG)KeServiceDescriptorTable->ServiceTableBase+0x55 * 4;   TenNtSetContextThread = *(ULONG*)uNtSetContextThreadAddress;TenNtGetContextThread = *(ULONG*)uNtGetContextThreadAddress;   KdPrint(("Set地址:%0X\n",TenNtSetContextThread));KdPrint(("Get地址:%0X\n",TenNtGetContextThread));   KdPrint(("Process:%0X \n",(ULONG)p_MyHookAddress));KdPrint(("Thread:%0X \n",(ULONG)t_MyHookAddress));   WPOFF();//清除CR0//提升IRQL中断级Irql=KeRaiseIrqlToDpcLevel();   //完成SSDT HOOK*(ULONG*)uNtGetContextThreadAddress = (ULONG)MyNtGetThreadContext;*(ULONG*)uNtSetContextThreadAddress = (ULONG)MyNtSetThreadContext;   //恢复IrqlKeLowerIrql(Irql);WPON();    //恢复CR0   return STATUS_UNSUCCESSFUL; }

另外还有一些功能型的函数一并给出,省的大家迷糊
我也算服务到位了,再看上面代码迷糊的时候。看这里找找
看看有没有能用到的,或者翻一下我以往的帖子。里面应该有
代码:
//保存5字节代码的结构#pragma pack(1)typedef struct _TOP5CODE{UCHARinstruction;//指令ULONGaddress;    //地址}TOP5CODE,*PTOP5CODE;#pragma pack( ) //ssdt表结构typedef struct _ServiceDescriptorTable {PVOID ServiceTableBase;    //System Service Dispatch Table 的基地址    PVOID ServiceCounterTable;//包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新。   unsigned int NumberOfServices;//由 ServiceTableBase 描述的服务的数目。    PVOID ParamTableBase; //包含每个系统服务参数字节数表的基地址-系统服务参数表 }*PServiceDescriptorTable;//由SSDT索引号获取当前函数地址//NtOpenProcess[+0x7A*4]extern PServiceDescriptorTable KeServiceDescriptorTable; ////////////////////////////////////////////////////////////////////////名称:MyGetFunAddress//功能:获取函数地址//参数:函数名称字符串指针//返回:函数地址//////////////////////////////////////////////////////////////////////ULONG MyGetFunAddress( IN PCWSTR FunctionName){UNICODE_STRING UniCodeFunctionName;RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );   } ////////////////////////////////////////////////////////////////////////名称:myGetCurrentAddress//功能:获取SSDT表中指定函数的当前地址//参数:index:指定函数在表中的索引号//返回:地址//////////////////////////////////////////////////////////////////////ULONG myGetCurrentAddress(IN ULONG index){ULONG    SSDT_Cur_Addr;__asm{    pushebx    pusheax    mov    ebx,KeServiceDescriptorTable    mov    ebx,    mov    eax,index    shl    eax,2    add    ebx,eax    mov    ebx,    mov    SSDT_Cur_Addr,ebx    pop    eax    pop    ebx}   returnSSDT_Cur_Addr;} VOID WPOFF(){__asm{    cli    mov eax,cr0    and eax,not 10000h    mov cr0,eax}} VOID WPON(){__asm{    mov eax,cr0    or eax,10000h    mov cr0,eax    sti}}
记在最后面的话:大家要善用搜索引擎(建议学习google hacking技巧),勤做笔记,最后要说的依然是感谢,感谢GOOGLE\BAIDU\PEDIY\DEBUGMAN。还有那些默默发帖的人~

如果有时间的话,我会将其他几个游戏保护的分析资料也放出来
什么GPK\HP\HS的。大家不要催不要急,一定会放出来的。等到我觉得这些东西都没有挑战性的时候那么也就不会再有资料陆续放出来了……
如果有好东西记得与我分享哈http://bbs.pediy.com/images/smilies/tongue.gifhttp://bbs.pediy.com/images/smilies/tongue.gifhttp://bbs.pediy.com/images/smilies/tongue.gif
http://bbs.pediy.com/attachment.php?attachmentid=52308&thumb=1&d=1292783798

Hmily 发表于 2016-8-16 16:48

请在看雪论坛给hmilywen发一个短消息确认是本人申请。

发表于 2016-8-16 18:11

Hmily 发表于 2016-8-16 16:48
请在看雪论坛给hmilywen发一个短消息确认是本人申请。

Yes, I did send message to you, please check again

Hmily 发表于 2016-8-17 15:08

ID:钱木头
邮箱:460874@qq.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

钱木头 发表于 2016-8-18 15:03

报道了,用的是什么服务器。弄个密码折腾一身汗

Hmily 发表于 2016-8-19 10:11

钱木头 发表于 2016-8-18 15:03
报道了,用的是什么服务器。弄个密码折腾一身汗

密码要求复杂度?

发表于 2016-8-22 11:45

Hmily 发表于 2016-8-19 10:11
密码要求复杂度?

每次点击都是502错误

Hmily 发表于 2016-8-22 14:31

游客 119.113.242.x 发表于 2016-8-22 11:45
每次点击都是502错误

密码那不应该有什么502啊,现在好了吗?
页: [1]
查看完整版本: 申请会员ID:钱木头【申请通过】