研究驱动有一个多月了,准备实践一下,拿DNF的TX驱动开刀,说干就干,刚开始心里没底,在看雪论坛找了很多相关的帖子,但是好象都比较早,直接拿来用是不行,但是我觉得思路绝对是对的,只有摸着石头过河,先把我的方法贴出来请各位大牛指教,在最后随便请教一下其他问题..

我过TX驱动是为了能用windbg调试DNF,我把他分成几个部分来做:
1.恢复OpenProcess, OpenThread,ReadVirtualMemory,WriteVirtualMemory
2.修改读写DebugPort的相关API
3.修复AttachProcess
步骤应该和以前是一样,但是方法变了.

我做一个TX驱动保护恢复工具(由于要时不时的看看ssdt和对比反汇编索性做在一起了,后面有下载地址),在工具启动的时候(打开游戏之前)做一些数据初始化和收集工作,并在进入游戏之后在点恢复键做真正的恢复工作.
首先是工具启动做的事情:
1.  ReadVirtualMemory,WriteVirtualMemory这两个函数现在没有保护,在工具启动的时候复制前16个字节保存:
ULONG AddrRead = (ULONG)KeServiceDescriptorTable->pSSDTBase + 0xBA * 4;
ULONG AddrWrite = (ULONG)KeServiceDescriptorTable->pSSDTBase + 0x115 * 4;
//记录NtReadVirtualMemory/NtWriteVirtualMemory 前16 字节
OrgRead[0] = *(PULONG)(*(PULONG)AddrRead);
OrgRead[1] = *(PULONG)(*(PULONG)AddrRead + 4);
OrgRead[2] = *(PULONG)(*(PULONG)AddrRead + 8);
OrgRead[3] = *(PULONG)(*(PULONG)AddrRead + 12);
OrgWrite[0] = *(PULONG)(*(PULONG)AddrWrite);
OrgWrite[1] = *(PULONG)(*(PULONG)AddrWrite + 4);
OrgWrite[2] = *(PULONG)(*(PULONG)AddrWrite + 8);
OrgWrite[3] = *(PULONG)(*(PULONG)AddrWrite + 12);

恢复OpenProcess(OpenThread是一样的,只是跳转的地址需要重新计算):
OpenProcess只修改了一处call ObOpenObjectByPointer, 直接用原始的地址去恢复直接蓝屏,修改call ObOpenObjectByPointer前的push代码跳转到自制代码也是蓝屏,甚至原本想修改e9的跳转地址再跳回来也是蓝屏,后来我把心一横,直接修改了OpenProcess的前两个push指令改成一个一个jmp,跳到我的自制代码,自制代码的结构是前16个字节做好堆栈直接jmp到一个写好的C函数,C函数的作用的是比较是否DNF.exe进程在调用OpenProcess,这里要先说一下在工具初始化的时候我分配了整个OpenProcess两倍加16个字节的代码数组也就是0x285*2+16,初始化的时候我已经复制了一份原始的OpenProcess到数组里面,然后在游戏运行的时候又复制了一份已经被HOOK了的OpenProcess代码在里面,所以C函数的作用是如果是DNF.exe的进程则进第二份被HOOK了的代码,否则进原始的代码:
UCHAR MyThreadRoot[ThreadLength*2+16], MyProcessRoot[ProcessLength*2+16];
// 游戏加载前,保存一次原始的函数代码
BufferCode(MyThreadRoot+16, (ULONG)OldThread, ThreadLength);
BufferCode(MyProcessRoot+16, (ULONG)OldProcess, ProcessLength);
// 游戏加载后,保存一次被HOOK了的函数代码
BufferCode(MyThreadRoot + ThreadLength + 16, (ULONG)OldThread, ThreadLength);
BufferCode(MyProcessRoot + ProcessLength + 16, (ULONG)OldProcess, ProcessLength);
// 修改OpenProcess前几个字节跳到自己的MyOpenProcess
ProcessCode[0] = *((BYTE*)OldProcess+0);
ProcessCode[1] = *((BYTE*)OldProcess+1);
ProcessCode[2] = *((BYTE*)OldProcess+2);
ProcessCode[3] = *((BYTE*)OldProcess+3);
ProcessCode[4] = *((BYTE*)OldProcess+4);
ProcessCode[5] = *((BYTE*)OldProcess+5);
addr1 = (ULONG)(UCHAR*)(MyProcessRoot) - (ULONG)OldProcess - 5;
*(BYTE*)((ULONG)OldProcess)  = 0xe9;
*((BYTE*)((ULONG)OldProcess)+1) = *(BYTE*)(&addr1);
*((BYTE*)((ULONG)OldProcess)+2) = *((BYTE*)(&addr1)+1);
*((BYTE*)((ULONG)OldProcess)+3) = *((BYTE*)(&addr1)+2);
*((BYTE*)((ULONG)OldProcess)+4) = *((BYTE*)(&addr1)+3);
*((BYTE*)((ULONG)OldProcess)+5) = 0x90;
// 下面是上面跳转到的自制代码
// 这里相当于前面提到的代码数组的前16个字节,需要再跳到C函数进行上面提到的那个判断然后在选// 择跳到原始代码还是被HOOK了的代码
addr1 = (ULONG)IsDNFprocess - (ULONG)MyProcessRoot - 5;
*(BYTE*)((ULONG)MyProcessRoot) = 0xe8;
*((BYTE*)((ULONG)MyProcessRoot)+1) = *(BYTE*)(&addr1);
*((BYTE*)((ULONG)MyProcessRoot)+2) = *((BYTE*)(&addr1)+1);
*((BYTE*)((ULONG)MyProcessRoot)+3) = *((BYTE*)(&addr1)+2);
*((BYTE*)((ULONG)MyProcessRoot)+4) = *((BYTE*)(&addr1)+3);
addr1 = (ULONG)(MyProcessRoot + ProcessLength + 16) - (ULONG)(UCHAR*)(MyProcessRoot + 7) - 6;
*((BYTE*)((ULONG)MyProcessRoot)+5) = 0x85;
*((BYTE*)((ULONG)MyProcessRoot)+6) = 0xc0;
*((BYTE*)((ULONG)MyProcessRoot)+7) = 0x0f;
*((BYTE*)((ULONG)MyProcessRoot)+8) = 0x84;
*((BYTE*)((ULONG)MyProcessRoot)+9) = *(BYTE*)(&addr1);
*((BYTE*)((ULONG)MyProcessRoot)+10) = *((BYTE*)(&addr1)+1);
*((BYTE*)((ULONG)MyProcessRoot)+11) = *((BYTE*)(&addr1)+2);
*((BYTE*)((ULONG)MyProcessRoot)+12) = *((BYTE*)(&addr1)+3);
// 下面是那个判断是否是DNF.exe进程的C函数
int IsDNFprocess() {
  char Name[16];
  if(GetProcessName(Name)) {
    if (strcmp(Name,"DNF.exe")) {
      dprintf("不是指定进程在调用!\n");
      return 1;
    }
    else {

      dprintf("是指定进程在调用!\n");
      return 0;
    }
  }
  else {
    dprintf("未获取到进程名!\n");
    return 2;
  }
}
// 下面是上面IsDNFprocess函数调用到的几个函数
ULONG GetProcessNameOffset() {
  int i=0;
  PEPROCESS curproc;
  DWORD procNameOffset;
  curproc = PsGetCurrentProcess();
  for(; i< 4096; i++) {
    if( !strncmp( "System", (PCHAR) curproc + i, strlen("System") )) {    
      procNameOffset = i;
      return procNameOffset;
    }
  }
  return 0;
}
BOOL GetProcessName( PCHAR theName ) {
  PEPROCESS       curproc;
  char            *nameptr;
  ULONG           i;
  KIRQL           oldirql;
  if( g_ProcessNameOffset ) {
    curproc = PsGetCurrentProcess();
    nameptr   = (PCHAR) curproc + g_ProcessNameOffset;
    strncpy( theName, nameptr, 16 );
    theName[15] = '\0'; /**//* NULL at end */
    return TRUE;
  }
  return FALSE;
}
还有一个重要的处理是将被HOOK了OpenProcess中call ObOpenObjectByPointer地址提取出来重新计算代码数组第二段的call ObOpenObjectByPointer(因为e8是相对地址所以要再计算一次..)

接下来是恢复DebugPort:
还是老办法将nt!_eprocess+0xbc转移到其他地址,我尝试了0x70,0x74,我发现经常被修改,如果将DebugPort对象地址保存到里面被修改之后再由nt!PsGetProcessDebugPort返回出来直接蓝屏,我用的0x78这个地址好象没怎么被修改.
找DebugPort相关是函数我是直接KD调试虚拟机,然后先设置硬件写断点,然后在虚拟机里面随便调试一个进程,ba w4 nt!_eprocess+0xdc,找出所有修改了这个地址的API,然后再找读的,读和写是一个方法.
找到之后我整理了一下有如下几个(我看到有人找的和我找的很多不一样,我百思不得其解,这也是我最后要请教的问题):
// 自己跟踪出来的地址(下面用的0x74我已经换成了0x78)

1  nt!DbgkpSetProcessDebugObject+0x6a        8063a930
8063a98e 8b450c          mov     eax,dword ptr [ebp+0Ch]
8063a991 8b4d14          mov     ecx,dword ptr [ebp+14h]
8063a994 8987bc000000    mov     dword ptr [edi+0BCh],eax
Change:   898774000000       {0x89,0x87,0x74,0x00,0x00,0x00}

2  nt!DbgkCopyProcessDebugPort+0xf
  80639993 8b4508          mov     eax,dword ptr [ebp+8]
80639996 83a0bc00000000  and     dword ptr [eax+0BCh],0


1  nt!DbgkpSetProcessDebugObject+0x5c        8063a930
  8063a980 c645ff01        mov     byte ptr [ebp-1],1
8063a984 ffd6            call      esi
8063a986 399fbc000000    cmp     dword ptr [edi+0BCh],ebx
Change:   399f74000000      {0x39,0x9f,0x74,0x00,0x00,0x00}

2  nt!DbgkpMarkProcessPeb+0x48          806398fa
  80639937 897dfc          mov     dword ptr [ebp-4],edi
8063993a 33c0            xor     eax,eax
8063993c 39bebc000000    cmp     dword ptr [esi+0BCh],edi
Change:  39be74000000      {0x39,0xbe,0x74,0x00,0x00,0x00}  

3  nt!DbgkCreateThread+0x12b            8063b08c
  8063b1b1 399ebc000000    cmp     dword ptr [esi+0BCh],ebx
  Change:   399e74000000      {0x39,0x9e,0x74,0x00,0x00,0x00}


4  nt!DbgkpQueueMessage+0x81:          80639bec
  80639c64 8b4508          mov     eax,dword ptr [ebp+8]
80639c67 8b80bc000000    mov     eax,dword ptr [eax+0BCh]
Change:  8b8074000000      {0x8b,0x80,0x74,0x00,0x00,0x00}

5  nt!PsGetProcessDebugPort+0xe          8052874c
8052874f 8bec            mov     ebp,esp
80528751 8b4508          mov     eax,dword ptr [ebp+8]
80528754 8b80bc000000    mov     eax,dword ptr [eax+0BCh]
Change:  8b8074000000      {0x8b,0x80,0x74,0x00,0x00,0x00}

6  nt!DbgkForwardException+0x44          8063aee0
  8063af1e 8b81bc000000    mov     eax,dword ptr [ecx+0BCh]
  Change:   8b8174000000      {0x8b,0x81,0x74,0x00,0x00,0x00}

7  nt!PspExitThread+0x28c              805c938a
  805c9606 7408            je      nt!PspExitThread+0x286 (805c9610)
805c9608 8b4de0          mov     ecx,dword ptr [ebp-20h]
805c960b e862a5f5ff      call    nt!ObfDereferenceObject (80523b72)
805c9610 399fbc000000    cmp     dword ptr [edi+0BCh],ebx
Change:  399f74000000        {0x39,0x9f,0x74,0x00,0x00,0x00}

8  nt!DbgkExitThread+0x26            8063b42a
  8063b441 f6804802000004  test    byte ptr [eax+248h],4
8063b448 7551            jne     nt!DbgkExitThread+0x71 (8063b49b)
8063b44a 8b89bc000000    mov     ecx,dword ptr [ecx+0BCh]
Change:  8b8974000000      {0x8b,0x89,0x74,0x00,0x00,0x00}

9  nt!KiDispatchException+0x18d          804fd94e
  804fdacc 64a124010000    mov     eax,dword ptr fs:[00000124h]
804fdad2 8b4044          mov     eax,dword ptr [eax+44h]
804fdad5 39b8bc000000    cmp     dword ptr [eax+0BCh],edi
Change:  39b874000000      {0x39,0xb8,0x74,0x00,0x00,0x00}

10  nt!DbgkpCloseObject+0x3e:
  8063a7bd 8b5d08          mov     ebx,dword ptr [ebp+8]
8063a7c0 81c3bc000000    add     ebx,0BCh
  Change:  81c374000000        {0x81,0xc3,0x74,0x00,0x00,0x00}

然后是修改相关API,我没有一个一个找特征码我怕漏了,我直接计算了KernelBase和KernelSize比如0x804d8000, 0x1f000000,我就在这区间里面直接替换。比如第10个API nt!DbgkpCloseObject我直接搜索区间里面所有的81c3bc000000替换为81c378000000,其他同理. 

最后是恢复KiAttachProcess
我查看的时候这里没有特殊的保护,所以我在工具初始化的时候保存了原始地址,然后直接恢复了该地址.

工具的下载地址(目前xp下能用):

bk_ssdtview.rar

结果是:我已经能用windbg附加DNF,但是还是收不到调试消息,我查看了debugport已经成功转移到了eprocess+0x78并且未被清,所有的API也已经使用了该地址,但是windbg还是显示挂起30秒那个提示,请各位给点提示!
随便请那位大哥给个邀请码,感谢!