八月的樱花 发表于 2012-10-18 20:32:34

最新过TX驱动保护,恢复debugport相关函数。

研究驱动有一个多月了,准备实践一下,拿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 = *(PULONG)(*(PULONG)AddrRead);
OrgRead = *(PULONG)(*(PULONG)AddrRead + 4);
OrgRead = *(PULONG)(*(PULONG)AddrRead + 8);
OrgRead = *(PULONG)(*(PULONG)AddrRead + 12);
OrgWrite = *(PULONG)(*(PULONG)AddrWrite);
OrgWrite = *(PULONG)(*(PULONG)AddrWrite + 4);
OrgWrite = *(PULONG)(*(PULONG)AddrWrite + 8);
OrgWrite = *(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, MyProcessRoot;
// 游戏加载前,保存一次原始的函数代码
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 = *((BYTE*)OldProcess+0);
ProcessCode = *((BYTE*)OldProcess+1);
ProcessCode = *((BYTE*)OldProcess+2);
ProcessCode = *((BYTE*)OldProcess+3);
ProcessCode = *((BYTE*)OldProcess+4);
ProcessCode = *((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;
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 = '\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)

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

2nt!DbgkCopyProcessDebugPort+0xf
80639993 8b4508          mov   eax,dword ptr
80639996 83a0bc00000000and   dword ptr ,0


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

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

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


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

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

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

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

8nt!DbgkExitThread+0x26            8063b42a
8063b441 f6804802000004test    byte ptr ,4
8063b448 7551            jne   nt!DbgkExitThread+0x71 (8063b49b)
8063b44a 8b89bc000000    mov   ecx,dword ptr
Change:8b8974000000      {0x8b,0x89,0x74,0x00,0x00,0x00}

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

10nt!DbgkpCloseObject+0x3e:
8063a7bd 8b5d08          mov   ebx,dword ptr
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
我查看的时候这里没有特殊的保护,所以我在工具初始化的时候保存了原始地址,然后直接恢复了该地址.

2920120 发表于 2012-10-19 03:31:59

下载低脂奶
页: [1]
查看完整版本: 最新过TX驱动保护,恢复debugport相关函数。