打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
现学现用之windbg的高级玩法外篇二:干掉QQProtect.sys


来自看雪论坛@PEdiy.com          
链 接: http://bbs.pediy.com/showthread.php?t=179566
作 者: ddlx
前言
今天与大家一起见证怎么废了QQProtect.sys。

环境:
QQ版本:2013正式版 sp2(8178)
调试工具:windbg 6.12
操作系统:xp sp3 32bit
其他工具:lordpe

一直很纳闷,你一个QQ干嘛还要上个保护驱动那?这种行为让楼主很不爽。已知QQProtect会启动内核线程、注册内核通知回调例程、SSDT hook, SSDTShadow hook, inline hook。
打算用 QQProtect来练练手,正好好久没玩内核了,快生疏了。
本篇共分为4部分:
1. kill 内核线程
2. 恢复SSDT hook,inline hook
3. 摘除内核通知回调例程
4. 恢复SSDTShadow hook


当然以上的操作都是使用windbg做的。一起来调戏QQProtect吧。


使用APC KILL内核线程

QQ的保护驱动创建了一些内核线程。楼主找到QQ的内核线程
获取system进程的EPROCESS地址
代码:
0: kd> !process 0 0 systemPROCESS 867b5830  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000    DirBase: 06ca0020  ObjectTable: e1002e40  HandleCount: 401.    Image: System
在获取QQProtect驱动的起始地址和结束地址
代码:
0: kd> lm m qq*start    end        module nameeee0c000 eee36680   QQProtect   (deferred)    
遍历进程的线程链,找到起始地址在QQProtect模块空间的线程
获取需要的字段在结构中的偏移
代码:
0: kd> r @$t0=@@(#FIELD_OFFSET(nt!_EPROCESS, ThreadListHead))0: kd> r @$t1= @@(#FIELD_OFFSET(nt!_ETHREAD, ThreadListEntry))0: kd> r @$t2=@@(#FIELD_OFFSET(nt!_ETHREAD, StartAddress))
遍历线程链,找到QQ的内核线程
代码:
0: kd> !list "-t nt!_LIST_ENTRY.FLink -e -x \"r @$t3=@$extret-@$t1; r @$t4= @$t3+@$t2; r @$t5=poi(@$t4);.if(@@((unsigned long)@$t5>(unsigned long)0xeee0c000 && (unsigned long)@$t5<(unsigned long)0xeee36680)){r @$t3;dt -b nt!_ETHREAD Cid. @$t3; dds @$t4 l1;}; \" 867b5830+@$t0"r @$t3=@$extret-@$t1; r @$t4= @$t3+@$t2; r @$t5=poi(@$t4);.if(@@((unsigned long)@$t5>(unsigned long)0xeee0c000 && (unsigned long)@$t5<(unsigned long)0xeee36680)){r @$t3;dt -b nt!_ETHREAD Cid. @$t3; dds @$t4 l1;};  $t3=86699130 //ETHREAD地址   +0x1ec Cid  :       +0x000 UniqueProcess : 0x00000004 //进程ID      +0x004 UniqueThread : 0x00000160  //线程ID86699354  eee11a0c QQProtect+0x5a0c //线程的起始地址r @$t3=@$extret-@$t1; r @$t4= @$t3+@$t2; r @$t5=poi(@$t4);.if(@@((unsigned long)@$t5>(unsigned long)0xeee0c000 && (unsigned long)@$t5<(unsigned long)0xeee36680)){r @$t3;dt -b nt!_ETHREAD Cid. @$t3; dds @$t4 l1;};  $t3=862de020   +0x1ec Cid  :       +0x000 UniqueProcess : 0x00000004       +0x004 UniqueThread : 0x00000164 862de244  eee22626 QQProtect+0x16626
通过上面的查找,找到了两个QQProtect的内核线程。找到了线程,接下来该怎么办呢?我们知道用户态线程直接强杀就OK了,但是内核线程强杀是不行的。通常使用APC。使用APC机制结束掉线程。
找到方法,那下一步就是实施了。由于不能调用系统API构建APC,只能手动构建和插入APC了。


第一步先找一块用来构建APC的内存。就在QQProtect中找一处吧。
先看一下QQProtect模块的区段信息
代码:
0: kd> !dh -s eee0c000SECTION HEADER #1   .text name   1B516 virtual size     480 virtual address   1B580 size of raw data     480 file pointer to raw data       0 file pointer to relocation table       0 file pointer to line numbers       0 number of relocations       0 number of line numbers68000020 flags         Code         Not Paged         (no align specified)         Execute ReadSECTION HEADER #2  .rdata name    3A8C virtual size   1BA00 virtual address    3B00 size of raw data   1BA00 file pointer to raw data       0 file pointer to relocation table       0 file pointer to line numbers       0 number of relocations       0 number of line numbers48000040 flags         Initialized Data         Not Paged         (no align specified)         Read OnlyDebug Directories(1)    Type       Size     Address  Pointer    cv           8f       1e868    1e868    Format: RSDS, guid, 1, f:\qqprotectdrvbuild\qqbuilder_qd3.5.1_drv2.9\basic_qqprotectdrv_vob\qqprotectdrv\objfre_wxp_x86\i386\QQProtectSYS.pdbSECTION HEADER #3   .data name    82AC virtual size   1F500 virtual address    8300 size of raw data   1F500 file pointer to raw data       0 file pointer to relocation table       0 file pointer to line numbers       0 number of relocations       0 number of line numbersC8000040 flags         Initialized Data         Not Paged //不分页内存         (no align specified)         Read WriteSECTION HEADER #4   INIT name     CC6 virtual size   27800 virtual address     D00 size of raw data   27800 file pointer to raw data       0 file pointer to relocation table       0 file pointer to line numbers       0 number of relocations       0 number of line numbersE2000020 flags         Code         Discardable //可废弃的,初始化完成后,内核可以回收这块内存。                                     //但是由于内核的页粒度为0x1000,INIT段的开始处一部分内存与.data段在同一块内存页中,那此段的前0x200个字节就是理想的APC数据块载体了         (no align specified)         Execute Read WriteSECTION HEADER #5   .rsrc name     310 virtual size   28500 virtual address     380 size of raw data   28500 file pointer to raw data       0 file pointer to relocation table       0 file pointer to line numbers       0 number of relocations       0 number of line numbers42000040 flags         Initialized Data         Discardable //模块加载完成后,此块的内存就被回收了         (no align specified)         Read OnlySECTION HEADER #6  .reloc name    1D90 virtual size   28880 virtual address    1E00 size of raw data   28880 file pointer to raw data       0 file pointer to relocation table       0 file pointer to line numbers       0 number of relocations       0 number of line numbers42000040 flags         Initialized Data         Discardable //模块加载完成后,此块的内存就被回收了         (no align specified)         Read Only
眼前一亮,发现了INIT段。我们就在INIT段中构建APC吧。
INIT起始RVA=27800
代码:
0: kd> ? eee0c000+27800 Evaluate expression: -287098880 = eee33800
//下面咱们就在eee33800地址处构建APC,已经知道QQ创建了两个线程,需要构建两个APC数据库+一个APC回调函数。
//楼主打算在eee33800处构建回调函数,在eee33900构建第一个线程的APC,在eee33950构建第二个线程的APC
//初始化200字节内存
代码:
0: kd> .for(r @$t1=0;@$t1<200;r @$t1=@$t1+4) {ed eee33800+@$t1 0;}
//获取pspExitthread符号地址
代码:
0: kd> x nt!pspExitthread805d3086 nt!PspExitThread = <no type information>
//构造APC回调函数,函数很简单,直接调用nt!pspExitthread
代码:
0: kd> a eee33800eee33800 push 748push 748eee33805 call 805d3086 call 805d3086 eee3380a ret 0n20ret 0n20eee3380d 
//看一下构建好的函数
代码:
0: kd> uf eee33800QQProtect+0x27800:eee33800 6848070000      push    748heee33805 e87cf87991      call    nt!PspExitThread (805d3086)eee3380a c21400          ret     14h
//构造第一个线程的APC,并插入原始APC队列里面
代码:
0: kd> r @$t0=eee33900; r @$t1=86699130; r@$t2=eee33800;?? ((nt!_KAPC*)@$t0)->Type=18;?? ((nt!_KAPC*)@$t0)->Size=sizeof(nt!_KAPC);?? ((nt!_KAPC*)@$t0)->Thread=@$t1;?? ((nt!_KAPC*)@$t0)->KernelRoutine=@$t2;?? ((nt!_KAPC*)@$t0)->Inserted=1;r @$t3=@@(&(((nt!_ETHREAD*)@$t1)->Tcb.ApcState.ApcListHead[0]));r @$t4=@@(&(((nt!_KAPC*)@$t0)->ApcListEntry));r @$t5=@@(((nt!_LIST_ENTRY*)@$t3)->Flink);?? ((nt!_LIST_ENTRY*)@$t4)->Flink=@$t5;?? ((nt!_LIST_ENTRY*)@$t4)->Blink=@$t3;?? ((nt!_LIST_ENTRY*)@$t5)->Blink=@$t4;?? ((nt!_LIST_ENTRY*)@$t3)->Flink=@$t4;?? ((nt!_ETHREAD*)@$t1)->Tcb.ApcState.KernelApcPending=1;
看一下上一条指令执行结果
代码:
0: kd> dt nt!_KAPC @$t0   +0x000 Type             : 0n18 //ApcObject=18   +0x002 Size             : 0n48 //nt!_KAPC结构大小   +0x004 Spare0           : 0   +0x008 Thread           : 0x86699130 _KTHREAD //所属线程   +0x00c ApcListEntry     : _LIST_ENTRY [ 0x86699164 - 0x86699164 //用来插入线程APC队列   +0x014 KernelRoutine    : 0xeee33800     void  +0 //APC内核回调函数指针   +0x018 RundownRoutine   : (null)    +0x01c NormalRoutine    : (null)    +0x020 NormalContext    : (null)    +0x024 SystemArgument1  : (null)    +0x028 SystemArgument2  : (null)    +0x02c ApcStateIndex    : 0 ''   +0x02d ApcMode          : 0 ''   +0x02e Inserted         : 0x1 '' //已插入0: kd> dt -b nt!_KTHREAD ApcState. @$t1   +0x034 ApcState  :       +0x000 ApcListHead :        [00] _LIST_ENTRY [ 0xeee3390c - 0xeee3390c ] //原始APC列表       [01]  [ 0x8669916c - 0x8669916c ]      +0x010 Process   : 0x867b5830       +0x014 KernelApcInProgress : 0 ''      +0x015 KernelApcPending : 0x1 '' //需要处理APC标志      +0x016 UserApcPending : 0 ''   +0x138 ApcStatePointer :     [00]     [01]    +0x165 ApcStateIndex : 0 ''
//构造第二个线程的APC,并插入原始APC队列里面
代码:
0: kd> r @$t0=eee33950; r @$t1=862de020; r@$t2=eee33800;?? ((nt!_KAPC*)@$t0)->Type=18;?? ((nt!_KAPC*)@$t0)->Size=sizeof(nt!_KAPC);?? ((nt!_KAPC*)@$t0)->Thread=@$t1;?? ((nt!_KAPC*)@$t0)->KernelRoutine=@$t2;?? ((nt!_KAPC*)@$t0)->Inserted=1;r @$t3=@@(&(((nt!_ETHREAD*)@$t1)->Tcb.ApcState.ApcListHead[0]));r @$t4=@@(&(((nt!_KAPC*)@$t0)->ApcListEntry));r @$t5=@@(((nt!_LIST_ENTRY*)@$t3)->Flink);?? ((nt!_LIST_ENTRY*)@$t4)->Flink=@$t5;?? ((nt!_LIST_ENTRY*)@$t4)->Blink=@$t3;?? ((nt!_LIST_ENTRY*)@$t5)->Blink=@$t4;?? ((nt!_LIST_ENTRY*)@$t3)->Flink=@$t4;?? ((nt!_ETHREAD*)@$t1)->Tcb.ApcState.KernelApcPending=1;
看一下上一条指令执行结果
代码:
0: kd> dt nt!_KAPC @$t0;dt -b nt!_KTHREAD ApcState. @$t1;   +0x000 Type             : 0n18   +0x002 Size             : 0n48   +0x004 Spare0           : 0   +0x008 Thread           : 0x862de020 _KTHREAD   +0x00c ApcListEntry     : _LIST_ENTRY [ 0x862de054 - 0x862de054 ]   +0x014 KernelRoutine    : 0xeee33800     void  +0   +0x018 RundownRoutine   : (null)    +0x01c NormalRoutine    : (null)    +0x020 NormalContext    : (null)    +0x024 SystemArgument1  : (null)    +0x028 SystemArgument2  : (null)    +0x02c ApcStateIndex    : 0 ''   +0x02d ApcMode          : 0 ''   +0x02e Inserted         : 0x1 ''   +0x034 ApcState  :       +0x000 ApcListHead :        [00] _LIST_ENTRY [ 0xeee3395c - 0xeee3395c ]       [01]  [ 0x862de05c - 0x862de05c ]      +0x010 Process   : 0x867b5830       +0x014 KernelApcInProgress : 0 ''      +0x015 KernelApcPending : 0x1 ''      +0x016 UserApcPending : 0 ''   +0x138 ApcStatePointer :     [00]     [01]    +0x165 ApcStateIndex : 0 ''
恢复SSDT和inline hook

获取SSDT和SSDTShadow地址表及大小
代码:
0: kd> dp nt!KeServiceDescriptorTableShadow l88055d6c0  80505450 00000000 0000011c 805058c48055d6d0  bf999b80 00000000 0000029b bf99a890
我们可以看到SSDT地址表起始地址为80505450,函数个数为0x11c
我们可以看到SSDTShadow地址表起始地址为bf999b80,函数个数为0x29b

看一下QQ HOOK了几个函数
SSDT HOOK
代码:
0: kd> dps 80505450 l11c... //太多了,忽略一部分805054e0  8061795a nt!NtCreateEventPair805054e4  eee1b6f4 QQProtect+0xf6f4805054e8  80579a62 nt!NtCreateIoCompletion...80505520  805c49b6 nt!NtCreateSymbolicLinkObject80505524  eee16768 QQProtect+0xa76880505528  80617622 nt!NtCreateTimer...80505544  806170d6 nt!NtCancelDeviceWakeupRequest80505548  eee1b58a QQProtect+0xf58a8050554c  80624c16 nt!NtDeleteKey...8050561c  80617a32 nt!NtOpenEventPair80505620  eee1b896 QQProtect+0xf89680505624  80579b3a nt!NtOpenIoCompletion...80505634  805f541a nt!NtOpenObjectAuditAlarm80505638  eee208d2 QQProtect+0x148d28050563c  805ee722 nt!NtOpenProcessToken...80505670  805f4918 nt!NtPrivilegedServiceAuditAlarm80505674  eee11b3e QQProtect+0x5b3e80505678  8060f7ba nt!NtPulseEvent8050567c  eee212e2 QQProtect+0x152e280505680  806170e4 nt!NtEnumerateBootEntries...8050571c  8057ccea nt!NtQueryVolumeInformationFile80505720  eee21808 QQProtect+0x1580880505724  80545eb4 nt!NtRaiseException...80505734  805a6e50 nt!NtReadRequestData80505738  eee10b76 QQProtect+0x4b768050573c  805d3754 nt!NtRegisterThreadTerminatePort...805057a0  806170e4 nt!NtEnumerateBootEntries805057a4  eee20d54 QQProtect+0x14d54805057a8  80646ce0 nt!NtSetDebugFilterState...805057cc  806439f2 nt!NtSetInformationDebugObject805057d0  eee1b4ce QQProtect+0xf4ce805057d4  805d7928 nt!NtSetInformationJobObject...80505848  805d58b0 nt!NtSuspendThread8050584c  eee20e38 QQProtect+0x14e3880505850  805d84bc nt!NtTerminateJobObject80505854  eee20a0a QQProtect+0x14a0a80505858  805d3b98 nt!NtTerminateThread...805058a0  805a6e78 nt!NtWriteRequestData805058a4  eee10fa4 QQProtect+0x4fa4805058a8  80505ad8 nt!NtYieldExecution...
擦这货HOOK的地方还真不少,由于我的测试环境比较干净,只看到QQ的HOOK。SSDT被 HOOK了14



再看一下SSDTShadow HOOK
代码:
0: kd> dps bf999b80 l29bbf999b80  ????????bf999b84  ????????bf999b88  ????????bf999b8c  ????????bf999b90  ????????bf999b94  ????????bf999b98  ????????bf999b9c  ????????bf999ba0  ????????bf999ba4  ????????bf999ba8  ????????bf999bac  ????????bf999bb0  ????????bf999bb4  ????????bf999bb8  ????????
为啥都不能访问呢?看一下bf999b80所属地址
代码:
0: kd> !address bf999b80  bf800000 - 001c3000                                     Usage       KernelSpaceUsageImage          ImageName   win32k.sys
win32k.sys。win32k.sys是win32子系统内核模块,只有当前线程是win32k线程时才能看到win32k驱动内存,否则win32k被换到外存。那只有等会拦截一个属于win32的线程再做打算了。
既然知道HOOK 了SSDT,那怎么恢复呢?怎么知道HOOK前的值是神马呢?

代码:
0: kd> !chkimg -d nt    805054e4-805054e7  4 bytes - nt!KiServiceTable+94    [ 84 a0 57 80:f4 b6 e1 ee ]    80505524-80505527  4 bytes - nt!KiServiceTable+d4 (+0x40)    [ d4 1f 5d 80:68 67 e1 ee ]    80505548-8050554b  4 bytes - nt!KiServiceTable+f8 (+0x24)    [ 2c 7c 57 80:8a b5 e1 ee ]    80505620-80505623  4 bytes - nt!KiServiceTable+1d0 (+0xd8)    [ 82 b1 57 80:96 b8 e1 ee ]    80505638-8050563b  4 bytes - nt!KiServiceTable+1e8 (+0x18)    [ fc c3 5c 80:d2 08 e2 ee ]    80505674-80505677  4 bytes - nt!KiServiceTable+224 (+0x3c)    [ da 93 5b 80:3e 1b e1 ee ]    8050567c-8050567f  4 bytes - nt!KiServiceTable+22c (+0x08)    [ d6 7e 57 80:e2 12 e2 ee ]    80505720-80505723  4 bytes - nt!KiServiceTable+2d0 (+0xa4)    [ 32 22 5d 80:08 18 e2 ee ]    80505738-8050573b  4 bytes - nt!KiServiceTable+2e8 (+0x18)    [ 8a 52 5b 80:76 0b e1 ee ]    805057a4-805057a7  4 bytes - nt!KiServiceTable+354 (+0x6c)    [ f6 26 5d 80:54 0d e2 ee ]    805057d0-805057d3  4 bytes - nt!KiServiceTable+380 (+0x2c)    [ 10 c0 57 80:ce b4 e1 ee ]    8050584c-8050584f  4 bytes - nt!KiServiceTable+3fc (+0x7c)    [ 6e 87 61 80:38 0e e2 ee ]    80505854-80505857  4 bytes - nt!KiServiceTable+404 (+0x08)    [ 9e 39 5d 80:0a 0a e2 ee ]    805058a4-805058a7  4 bytes - nt!KiServiceTable+454 (+0x50)    [ 94 53 5b 80:a4 0f e1 ee ]    805a2cba-805a2cbd  4 bytes - nt!KeUserModeCallback+8    [ c2 9e f9 ff:c8 45 87 6e ]    805b3e0f-805b3e15  7 bytes - nt!MmUnmapViewOfSection+17 (+0x11155)    [ cc cc cc cc cc 8b ff:e9 bc bd 85 6e eb f9 ]67 errors : nt (805054e4-805b3e15)
上面使用!chkimg指令检查了nt模块的所有被修改的数据。(蓝色为原始值)
经过分析,共有对nt!KiServiceTable的HOOK 14处,nt!KiServiceTable存储的就是SSDT地址,也就是SSDT共被HOOK了14处,与刚才咱们看到的一致。那
    
代码:
805a2cba-805a2cbd  4 bytes - nt!KeUserModeCallback+8    [ c2 9e f9 ff:c8 45 87 6e ]    805b3e0f-805b3e15  7 bytes - nt!MmUnmapViewOfSection+17 (+0x11155)    [ cc cc cc cc cc 8b ff:e9 bc bd 85 6e eb f9 ]
这两个是神马?是inline hook!反汇编看一下
代码:
0: kd> u nt!KeUserModeCallbacknt!KeUserModeCallback:805a2cb2 6a30            push    30h805a2cb4 6800aa4d80      push    offset nt!KiDebugRegisterContextOffsets+0x24 (804daa00)805a2cb9 e8c845876e      call    QQProtect+0xb286 (eee17286)805a2cbe e8b9fbf5ff      call    nt!KiGetUserModeStackAddress (8050287c)805a2cc3 8945e4          mov     dword ptr [ebp-1Ch],eax805a2cc6 8b18            mov     ebx,dword ptr [eax]805a2cc8 895dd8          mov     dword ptr [ebp-28h],ebx805a2ccb 8365fc00        and     dword ptr [ebp-4],00: kd> u 805b3e0f nt!MmUnmapViewOfSection+0x17:805b3e0f e9bcbd856e      jmp     QQProtect+0x3bd0 (eee0fbd0)nt!NtUnmapViewOfSection:805b3e14 ebf9            jmp     nt!MmUnmapViewOfSection+0x17 (805b3e0f)805b3e16 55              push    ebp805b3e17 8bec            mov     ebp,esp805b3e19 51              push    ecx805b3e1a 56              push    esi805b3e1b 64a124010000    mov     eax,dword ptr fs:[00000124h]805b3e21 8a8040010000    mov     al,byte ptr [eax+140h]
原来是HOOK了nt!KeUserModeCallbacknt!NtUnmapViewOfSection。咱们再看看他都HOOK了哪些SSDT
代码:
0: kd> ln 8057a084;ln 805d1fd4;ln 80577c2c; ln 8057b182; ln 805cc3fc; ln 805b93da; ln 80577ed6; ln 805d2232;ln 805b528a; ln 805d26f6;ln 8057c010; ln 8061876e; ln 805d399e; ln 805b5394;    nt!NtCreateFile = <no type information>    nt!NtCreateThread = <no type information>    nt!NtDeleteFile = <no type information>    nt!NtOpenFile = <no type information>    nt!NtOpenProcess = <no type information>    nt!NtProtectVirtualMemory = <no type information>    nt!NtQueryAttributesFile = <no type information>    nt!NtQueueApcThread = <no type information>    nt!NtReadVirtualMemory = <no type information>    nt!NtSetContextThread = <no type information>    nt!NtSetInformationFile = <no type information>    nt!NtSystemDebugControl = <no type information>    nt!NtTerminateProcess = <no type information>    nt!NtWriteVirtualMemory = <no type information>
这个变态,HOOK了这么多。楼主要恢复他了
恢复SSDT
代码:
0: kd> eb nt!KiServiceTable+94 84 a0 57 80; eb nt!KiServiceTable+d4 d4 1f 5d 80; eb nt!KiServiceTable+f8 2c 7c 57 80; eb nt!KiServiceTable+1d0 82 b1 57 80; eb nt!KiServiceTable+1e8 fc c3 5c 80; eb nt!KiServiceTable+224 da 93 5b 80; eb nt!KiServiceTable+22c d6 7e 57 80;eb nt!KiServiceTable+2d0 32 22 5d 80;eb nt!KiServiceTable+2e8 8a 52 5b 80;eb nt!KiServiceTable+354 f6 26 5d 80;eb nt!KiServiceTable+380 10 c0 57 80;eb nt!KiServiceTable+3fc 6e 87 61 80;eb  nt!KiServiceTable+404 9e 39 5d 80;eb nt!KiServiceTable+454 94 53 5b 80;
恢复inline hook
代码:
0: kd> eb nt!KeUserModeCallback+8 c2 9e f9 ff; eb nt!MmUnmapViewOfSection+17 cc cc cc cc cc 8b ff;
看一下恢复的效果:
代码:
0: kd> !chkimg -d nt0 errors : nt 
Ok,恢复完毕


删除内核通知回调

咱们看看QQ有没有注册通知
代码:
0: kd> r @$t0=poi(nt!PspCreateProcessNotifyRoutineCount);r @$t1=nt!PspCreateProcessNotifyRoutine;.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}e170209c  f7710194 vmci!VMCI_DeviceGet+0x96e0: kd> r @$t0=poi(nt!PspCreateThreadNotifyRoutineCount);r @$t1=nt!PspCreateThreadNotifyRoutine;.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}e1612504  eee15f48 QQProtect+0x9f480: kd> r @$t0=poi(nt!PspLoadImageNotifyRoutineCount);r @$t1=nt!PspLoadImageNotifyRoutine;.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}e16249e4  eee190d8 QQProtect+0xd0d80: kd> r @$t0=poi(nt!CmpCallBackCount);r @$t1=nt!CmpCallBackVector;.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}e172be7c  eee0fa40 QQProtect+0x3a40
发现共注册了创建线程、加载模、注册表操作回调例程。现在把回调清除
代码:
0: kd> ed nt!PspCreateThreadNotifyRoutineCount 0; ed nt!PspCreateThreadNotifyRoutine 0;0: kd> ed nt!PspLoadImageNotifyRoutineCount 0; ed nt!PspLoadImageNotifyRoutine 0;0: kd> ed nt!CmpCallBackCount 0; ed nt!CmpCallBackVector 0;
Ok,这个时候,SSDT和inline hook恢复了 ,内核通知回调清除了,线程APC也设置了,继续执行就能终止线程。

代码:
0: kd> g
过一会,在按Ctrl+break断下,检查一下线程是否已终止
代码:
0: kd> r @$t0=@@(#FIELD_OFFSET(nt!_EPROCESS, ThreadListHead));r @$t1= @@(#FIELD_OFFSET(nt!_ETHREAD, ThreadListEntry));r @$t2=@@(#FIELD_OFFSET(nt!_ETHREAD, StartAddress));!list "-t nt!_LIST_ENTRY.FLink -e -x \"r @$t3=@$extret-@$t1; r @$t4= @$t3+@$t2; r @$t5=poi(@$t4);.if(@@((unsigned long)@$t5>(unsigned long)0xeee0c000 && (unsigned long)@$t5<(unsigned long)0xeee36680)){r @$t3;dt -b nt!_ETHREAD Cid. ExitStatus @$t3; dt -b nt!_KTHREAD Header. @$t3; }; \" 867b5830+@$t0"...r @$t3=@$extret-@$t1; r @$t4= @$t3+@$t2; r @$t5=poi(@$t4);.if(@@((unsigned long)@$t5>(unsigned long)0xeee0c000 && (unsigned long)@$t5<(unsigned long)0xeee36680)){r @$t3;dt -b nt!_ETHREAD Cid. ExitStatus @$t3; dt -b nt!_KTHREAD Header. @$t3; };  $t3=86699130   +0x1d0 ExitStatus : 0n1864 //0n1864=0x478   +0x1ec Cid        :       +0x000 UniqueProcess : 0x00000004       +0x004 UniqueThread : 0x00000160    +0x000 Header  :       +0x000 Type    : 0x6 ''      +0x001 Absolute : 0 ''      +0x002 Size    : 0x70 'p'      +0x003 Inserted : 0 ''      +0x004 SignalState : 0n1 //已终止,信号状态变成有信号      +0x008 WaitListHead : _LIST_ENTRY [ 0x86730028 - 0x86730028 ]r @$t3=@$extret-@$t1; r @$t4= @$t3+@$t2; r @$t5=poi(@$t4);.if(@@((unsigned long)@$t5>(unsigned long)0xeee0c000 && (unsigned long)@$t5<(unsigned long)0xeee36680)){r @$t3;dt -b nt!_ETHREAD Cid. ExitStatus @$t3; dt -b nt!_KTHREAD Header. @$t3; };  $t3=862de020   +0x1d0 ExitStatus : 0n1864   +0x1ec Cid        :       +0x000 UniqueProcess : 0x00000004       +0x004 UniqueThread : 0x00000164    +0x000 Header  :       +0x000 Type    : 0x6 ''      +0x001 Absolute : 0 ''      +0x002 Size    : 0x70 'p'      +0x003 Inserted : 0 ''      +0x004 SignalState : 0n1      +0x008 WaitListHead : _LIST_ENTRY [ 0x8665ba50 - 0x8665ba50 ]...
至此,此篇流水基本上结束了。差点忘了还有SSDTShadow没有看。

恢复SSDTShadow hook

我们现在nt!NtWaitForSingleObject 函数上下个断点,断下一个win32线程,就可以看到win32k模块内存了
代码:
0: kd> bp nt!NtWaitForSingleObject ".if(@@(((nt!_KTHREAD*)@$thread)->Win32Thread)!=0){;}.else{gc;}"breakpoint 0 redefined
下完断点继续执行
代码:
0: kd> g
不一会就断下来了
代码:
nt!NtWaitForSingleObject:805c16b6 6a2c            push    2Ch
看一下线程自己的ServiceTable字段
代码:
0: kd> dd @@(((nt!_KTHREAD*)@$thread)->ServiceTable) l88055d6c0  80505450 00000000 0000011c 805058c48055d6d0  bf999b80 00000000 0000029b bf99a890
看SSDTShadow表
代码:
0: kd> dds bf999b80 l29b...bf99a160  bf8f48da win32k!NtUserFillWindowbf99a164  bf81b5fa win32k!NtUserFindExistingCursorIconbf99a168  eee124fe QQProtect+0x64febf99a16c  bf91540e win32k!NtUserFlashWindowExbf99a170  bf8e8688 win32k!NtUserGetAltTabInfo...
发现Hook了一个函数

检查一下win32k
代码:
0: kd> !chkimg -d win32k0 errors : win32k 
竟然没有发现hook!为什么呢?经过分析(!dh命令),SSDTShadow表在win32k模块的.data段,SSDT在nt的.text段。看来!chkimg只对代码段做检查。
计算一下修改处地址的Rva
代码:
0: kd> ? bf99a168-bf800000Evaluate expression: 1679720 = 0019a168
Rva=0019a168
使用lordPE看一下0019a168处的值是多少

楼主看到是bf8b1369,计算Rva
代码:
0: kd> ? bf8b1369-bf800000Evaluate expression: 725865 = 000b1369
查看是哪个函数
代码:
0: kd> ln win32k+000b1369(bf8b1369)   win32k!NtUserFindWindowEx   |  (bf8b14cc)   win32k!_FindWindowExExact matches:    win32k!NtUserFindWindowEx = <no type information>
原来QQ hook的是win32k!NtUserFindWindowEx
这就好办了,恢复他
代码:
1: kd> ed bf99a168 win32k+000b1369
看一下恢复效果
代码:
1: kd> dds bf999b80 l29b...bf99a160  bf8f48da win32k!NtUserFillWindowbf99a164  bf81b5fa win32k!NtUserFindExistingCursorIconbf99a168  bf8b1369 win32k!NtUserFindWindowExbf99a16c  bf91540e win32k!NtUserFlashWindowExbf99a170  bf8e8688 win32k!NtUserGetAltTabInfo...
至此,SSDTShadow表也恢复了。
总结
1. 其实上面的所有操作都可以使用xuetr来完成。但是对于楼主学习内核来说,自己实际的操作才是了解windows操作系统最佳的途径。
2. 虽然杀死了内核线程,但是QQ在每次启动的时候还是再次挂钩SSDT hook, SSDTShadow hook, inline hook。有兴趣的自己调试实现方法并处理之。

        
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
eee
EEE
30篇预测翻译+2000翻译词汇 6级
RGB色彩对照表
强劲指标KDJ 三底背离,现学现用,一招制胜(附主图源码)
颜色和编码对照表
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服