[原创]现学现用之windbg的高级玩法外篇二:干掉QQProtect.sys
前言
今天是国庆节,先祝大家节日快乐。
今天与大家一起见证怎么废了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> [COLOR=Red]!process 0 0 system[/COLOR]
PROCESS [COLOR=Red][B]867b5830 [/B][/COLOR]SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 06ca0020 ObjectTable: e1002e40 HandleCount: 401.
Image: System在获取QQProtect驱动的起始地址和结束地址0: kd>[COLOR=Red] lm m qq*[/COLOR] start end module name [COLOR=Red]eee0c000 eee36680[/COLOR] QQProtect (deferred)遍历进程的线程链,找到起始地址在QQProtect模块空间的线程
获取需要的字段在结构中的偏移
0: kd>[COLOR=Red] r @$t0=@@(#FIELD_OFFSET(nt!_EPROCESS, ThreadListHead))[/COLOR] 0: kd> [COLOR=Red]r @$t1= @@(#FIELD_OFFSET(nt!_ETHREAD, ThreadListEntry))[/COLOR] 0: kd> [COLOR=Red]r @$t2=@@(#FIELD_OFFSET(nt!_ETHREAD, StartAddress))[/COLOR]遍历线程链,找到QQ的内核线程
0: kd>[COLOR=Red] !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)[B]0xeee0c000 [/B]&& (unsigned long)@$t5<(unsigned long)[B]0xeee36680[/B])){r @$t3;dt -b nt!_ETHREAD Cid. @$t3; dds @$t4 l1;}; \" [B]867b5830[/B]+@$t0"
[/COLOR]
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=[COLOR=Red]86699130 [/COLOR][COLOR=Blue]//ETHREAD地址[/COLOR]
+0x1ec Cid :
+0x000 UniqueProcess : 0x00000004[COLOR=Blue] //进程ID[/COLOR]
+0x004 UniqueThread : 0x00000160[COLOR=Blue] //线程ID[/COLOR]
86699354 eee11a0c [COLOR=Red]QQProtect+0x5a0c[/COLOR] [COLOR=Blue]//线程的起始地址[/COLOR]
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=[COLOR=Red]862de020[/COLOR]
+0x1ec Cid :
+0x000 UniqueProcess : 0x00000004
+0x004 UniqueThread : 0x00000164
862de244 eee22626 [COLOR=Red]QQProtect+0x16626[/COLOR]
通过上面的查找,找到了两个QQProtect的内核线程。找到了线程,接下来该怎么办呢?我们知道用户态线程直接强杀就OK了,但是内核线程强杀是不行的。通常使用APC。使用APC机制结束掉线程。找到方法,那下一步就是实施了。由于不能调用系统API构建APC,只能手动构建和插入APC了。
第一步先找一块用来构建APC的内存。就在QQProtect中找一处吧。
先看一下QQProtect模块的区段信息
0: kd>[COLOR=Red] !dh -s eee0c000[/COLOR]
SECTION HEADER #1
[COLOR=Blue].text[/COLOR] 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 numbers
68000020 flags
Code
Not Paged
(no align specified)
Execute Read
SECTION HEADER #2
[COLOR=Blue].rdata [/COLOR]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 numbers
48000040 flags
Initialized Data
Not Paged
(no align specified)
Read Only
Debug 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.pdb
SECTION HEADER #3
[COLOR=Blue].data[/COLOR] 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 numbers
C8000040 flags
Initialized Data
Not Paged //不分页内存
(no align specified)
Read Write
SECTION HEADER #4
[B][COLOR=Blue]INIT [/COLOR]name[/B]
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 numbers
E2000020 flags
Code
Discardable [COLOR=Blue]//可废弃的,初始化完成后,内核可以回收这块内存。[/COLOR]
[COLOR=Blue]//但是由于内核的页粒度为0x1000,INIT段的开始处一部分内存与.data段在同一块内存页中,那此段的前0x200个字节就是理想的APC数据块载体[/COLOR]了
(no align specified)
Execute Read Write
SECTION HEADER #5
[COLOR=Blue].rsrc [/COLOR]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 numbers
42000040 flags
Initialized Data
Discardable [COLOR=Blue]//模块加载完成后,此块的内存就被回收了[/COLOR]
(no align specified)
Read Only
SECTION HEADER #6
[COLOR=Blue] .reloc[/COLOR] 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 numbers
42000040 flags
Initialized Data
Discardable [COLOR=Blue]//模块加载完成后,此块的内存就被回收了[/COLOR]
(no align specified)
Read Only眼前一亮,发现了INIT段。我们就在INIT段中构建APC吧。INIT起始RVA=27800
0: kd> [COLOR=Red]? eee0c000+27800 [/COLOR] Evaluate expression: -287098880 = [COLOR=Red]eee33800[/COLOR]//下面咱们就在eee33800地址处构建APC,已经知道QQ创建了两个线程,需要构建两个APC数据库+一个APC回调函数。
//楼主打算在eee33800处构建回调函数,在eee33900构建第一个线程的APC,在eee33950构建第二个线程的APC
//初始化200字节内存
0: kd> [COLOR=Red].for(r @$t1=0;@$t1<200;r @$t1=@$t1+4) {ed [B]eee33800[/B]+@$t1 0;}[/COLOR]//获取pspExitthread符号地址0: kd> [COLOR=Red]x nt!pspExitthread[/COLOR] [B][COLOR=Red]805d3086 [/COLOR][/B]nt!PspExitThread = <no type information>//构造APC回调函数,函数很简单,直接调用nt!pspExitthread
0: kd> [COLOR=Red]a eee33800[/COLOR] eee33800 push 748 [COLOR=Red]push 748[/COLOR] eee33805 call 805d3086 [COLOR=Red]call 805d3086 [/COLOR] eee3380a ret 0n20 [COLOR=Red]ret 0n20[/COLOR] eee3380d//看一下构建好的函数
0: kd> [COLOR=Red]uf eee33800[/COLOR] QQProtect+0x27800: eee33800 6848070000 push 748h eee33805 e87cf87991 call nt!PspExitThread (805d3086) eee3380a c21400 ret 14h//构造第一个线程的APC,并插入原始APC队列里面
0: kd> [COLOR=Red]r @$t0=[B]eee33900[/B]; r @$t1=[B]86699130[/B]; r@$t2=[B]eee33800[/B];?? ((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;[/COLOR]看一下上一条指令执行结果
0: kd> [COLOR=Red]dt nt!_KAPC @$t0[/COLOR]
+0x000 Type : [COLOR=Red]0n18 [/COLOR][COLOR=Blue]//ApcObject=18[/COLOR]
+0x002 Size : [COLOR=Red]0n48 [/COLOR][COLOR=Blue]//nt!_KAPC结构大小[/COLOR]
+0x004 Spare0 : 0
+0x008 Thread : [COLOR=Red]0x86699130 [/COLOR]_KTHREAD [COLOR=Blue]//所属线程[/COLOR]
+0x00c ApcListEntry : _LIST_ENTRY [[COLOR=Red] 0x86699164 - 0x86699164 [/COLOR]] [COLOR=Blue]//用来插入线程APC队列[/COLOR]
+0x014 KernelRoutine : [COLOR=Red]0xeee33800 [/COLOR]void +0 [COLOR=Blue]//APC内核回调函数指针[/COLOR]
+0x018 RundownRoutine : (null)
+0x01c NormalRoutine : (null)
+0x020 NormalContext : (null)
+0x024 SystemArgument1 : (null)
+0x028 SystemArgument2 : (null)
+0x02c ApcStateIndex : 0 ''
+0x02d ApcMode : 0 ''
+0x02e Inserted : [COLOR=Red]0x1 [/COLOR]''[COLOR=Blue] //已插入[/COLOR]
0: kd>[COLOR=Red] dt -b nt!_KTHREAD ApcState. @$t1[/COLOR]
+0x034 ApcState :
+0x000 ApcListHead :
[00] _LIST_ENTRY [[COLOR=Red] 0xeee3390c - 0xeee3390c[/COLOR] ] [COLOR=Blue]//原始APC列表[/COLOR]
[01] [ 0x8669916c - 0x8669916c ]
+0x010 Process : 0x867b5830
+0x014 KernelApcInProgress : 0 ''
+0x015 KernelApcPending : [COLOR=Red]0x1 [/COLOR]'' [COLOR=Blue]//需要处理APC标志[/COLOR]
+0x016 UserApcPending : 0 ''
+0x138 ApcStatePointer :
[00]
[01]
+0x165 ApcStateIndex : 0 ''//构造第二个线程的APC,并插入原始APC队列里面0: kd> [COLOR=Red]r @$t0=[B]eee33950[/B]; r @$t1=[B]862de020[/B]; r@$t2=[B]eee33800[/B];?? ((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;[/COLOR]看一下上一条指令执行结果
0: kd> [COLOR=Red]dt nt!_KAPC @$t0;dt -b nt!_KTHREAD ApcState. @$t1;[/COLOR]
+0x000 Type : [COLOR=Red]0n18[/COLOR]
+0x002 Size : [COLOR=Red]0n48[/COLOR]
+0x004 Spare0 : 0
+0x008 Thread : [COLOR=Red]0x862de020 [/COLOR]_KTHREAD
+0x00c ApcListEntry : _LIST_ENTRY [ [COLOR=Red]0x862de054 - 0x862de054[/COLOR] ]
+0x014 KernelRoutine : [COLOR=Red]0xeee33800 [/COLOR]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 : [COLOR=Red]0x1 [/COLOR]''
+0x034 ApcState :
+0x000 ApcListHead :
[00] _LIST_ENTRY [ [COLOR=Red]0xeee3395c - 0xeee3395c[/COLOR] ]
[01] [ 0x862de05c - 0x862de05c ]
+0x010 Process : 0x867b5830
+0x014 KernelApcInProgress : 0 ''
+0x015 KernelApcPending : [COLOR=Red]0x1 [/COLOR]''
+0x016 UserApcPending : 0 ''
+0x138 ApcStatePointer :
[00]
[01]
+0x165 ApcStateIndex : 0 ''
恢复SSDT和inline hook获取SSDT和SSDTShadow地址表及大小
0: kd> [COLOR=Red]dp nt!KeServiceDescriptorTableShadow l8[/COLOR] 8055d6c0 [COLOR=Red] 80505450 [/COLOR]00000000 [COLOR=Red]0000011c [/COLOR]805058c4 8055d6d0 [COLOR=Red] bf999b80 [/COLOR]00000000 [COLOR=Red]0000029b [/COLOR]bf99a890我们可以看到SSDT地址表起始地址为80505450,函数个数为0x11c
我们可以看到SSDTShadow地址表起始地址为bf999b80,函数个数为0x29b
看一下QQ HOOK了几个函数
SSDT HOOK
0: kd> [COLOR=Red]dps 80505450 l11c[/COLOR] ... [COLOR=Blue]//太多了,忽略一部分[/COLOR] 805054e0 8061795a nt!NtCreateEventPair [B][COLOR=Blue]805054e4 eee1b6f4 QQProtect+0xf6f4[/COLOR][/B] 805054e8 80579a62 nt!NtCreateIoCompletion ... 80505520 805c49b6 nt!NtCreateSymbolicLinkObject [B][COLOR=Blue]80505524 eee16768 QQProtect+0xa768[/COLOR][/B] 80505528 80617622 nt!NtCreateTimer ... 80505544 806170d6 nt!NtCancelDeviceWakeupRequest [B][COLOR=Blue]80505548 eee1b58a QQProtect+0xf58a[/COLOR][/B] 8050554c 80624c16 nt!NtDeleteKey ... 8050561c 80617a32 nt!NtOpenEventPair [B][COLOR=Blue]80505620 eee1b896 QQProtect+0xf896[/COLOR][/B] 80505624 80579b3a nt!NtOpenIoCompletion ... 80505634 805f541a nt!NtOpenObjectAuditAlarm [COLOR=Blue][B]80505638 eee208d2 QQProtect+0x148d2[/B][/COLOR] 8050563c 805ee722 nt!NtOpenProcessToken ... 80505670 805f4918 nt!NtPrivilegedServiceAuditAlarm [B][COLOR=Blue]80505674 eee11b3e QQProtect+0x5b3e[/COLOR][/B] 80505678 8060f7ba nt!NtPulseEvent [B][COLOR=Blue]8050567c eee212e2 QQProtect+0x152e2[/COLOR][/B] 80505680 806170e4 nt!NtEnumerateBootEntries ... 8050571c 8057ccea nt!NtQueryVolumeInformationFile [B][COLOR=Blue]80505720 eee21808 QQProtect+0x15808[/COLOR][/B] 80505724 80545eb4 nt!NtRaiseException ... 80505734 805a6e50 nt!NtReadRequestData [B][COLOR=Blue]80505738 eee10b76 QQProtect+0x4b76[/COLOR][/B] 8050573c 805d3754 nt!NtRegisterThreadTerminatePort ... 805057a0 806170e4 nt!NtEnumerateBootEntries [B][COLOR=Blue]805057a4 eee20d54 QQProtect+0x14d54[/COLOR][/B] 805057a8 80646ce0 nt!NtSetDebugFilterState ... 805057cc 806439f2 nt!NtSetInformationDebugObject [B][COLOR=Blue]805057d0 eee1b4ce QQProtect+0xf4ce[/COLOR][/B] 805057d4 805d7928 nt!NtSetInformationJobObject ... [COLOR=Black]80505848 805d58b0 nt!NtSuspendThread[/COLOR][B][COLOR=Blue] 8050584c eee20e38 QQProtect+0x14e38[/COLOR][/B] 80505850 805d84bc nt!NtTerminateJobObject [COLOR=Blue][B]80505854 eee20a0a QQProtect+0x14a0a[/B][/COLOR] 80505858 805d3b98 nt!NtTerminateThread ... 805058a0 805a6e78 nt!NtWriteRequestData [COLOR=Blue][B]805058a4 eee10fa4 QQProtect+0x4fa4[/B][/COLOR] 805058a8 80505ad8 nt!NtYieldExecution ...擦这货HOOK的地方还真不少,由于我的测试环境比较干净,只看到QQ的HOOK。SSDT被 HOOK了14处
再看一下SSDTShadow HOOK
0: kd>[COLOR=Red] dps bf999b80 l29b[/COLOR] bf999b80 ???????? bf999b84 ???????? bf999b88 ???????? bf999b8c ???????? bf999b90 ???????? bf999b94 ???????? bf999b98 ???????? bf999b9c ???????? bf999ba0 ???????? bf999ba4 ???????? bf999ba8 ???????? bf999bac ???????? bf999bb0 ???????? bf999bb4 ???????? bf999bb8 ????????为啥都不能访问呢?看一下bf999b80所属地址
0: kd>[COLOR=Red] !address bf999b80[/COLOR]
bf800000 - 001c3000
Usage KernelSpaceUsageImage
ImageName win32k.syswin32k.sys。win32k.sys是win32子系统内核模块,只有当前线程是win32k线程时才能看到win32k驱动内存,否则win32k被换到外存。那只有等会拦截一个属于win32的线程再做打算了。既然知道HOOK 了SSDT,那怎么恢复呢?怎么知道HOOK前的值是神马呢?
0: kd> [COLOR=Red]!chkimg -d nt[/COLOR]
805054e4-805054e7 4 bytes - nt!KiServiceTable+94
[ [COLOR=Blue]84 a0 57 80[/COLOR]:f4 b6 e1 ee ]
80505524-80505527 4 bytes - nt!KiServiceTable+d4 (+0x40)
[[COLOR=Blue] d4 1f 5d 80[/COLOR]:68 67 e1 ee ]
80505548-8050554b 4 bytes - nt!KiServiceTable+f8 (+0x24)
[ [COLOR=Blue]2c 7c 57 80[/COLOR]:8a b5 e1 ee ]
80505620-80505623 4 bytes - nt!KiServiceTable+1d0 (+0xd8)
[ [COLOR=Blue]82 b1 57 80[/COLOR]:96 b8 e1 ee ]
80505638-8050563b 4 bytes - nt!KiServiceTable+1e8 (+0x18)
[ [COLOR=Blue]fc c3 5c 80[/COLOR]:d2 08 e2 ee ]
80505674-80505677 4 bytes - nt!KiServiceTable+224 (+0x3c)
[[COLOR=Blue] da 93 5b 80[/COLOR]:3e 1b e1 ee ]
8050567c-8050567f 4 bytes - nt!KiServiceTable+22c (+0x08)
[ [COLOR=Blue]d6 7e 57 80[/COLOR]:e2 12 e2 ee ]
80505720-80505723 4 bytes - nt!KiServiceTable+2d0 (+0xa4)
[ [COLOR=Blue]32 22 5d 80[/COLOR]:08 18 e2 ee ]
80505738-8050573b 4 bytes - nt!KiServiceTable+2e8 (+0x18)
[ [COLOR=Blue]8a 52 5b 80[/COLOR]:76 0b e1 ee ]
805057a4-805057a7 4 bytes - nt!KiServiceTable+354 (+0x6c)
[ [COLOR=Blue]f6 26 5d 80[/COLOR]:54 0d e2 ee ]
805057d0-805057d3 4 bytes - nt!KiServiceTable+380 (+0x2c)
[ [COLOR=Blue]10 c0 57 80[/COLOR]:ce b4 e1 ee ]
8050584c-8050584f 4 bytes - nt!KiServiceTable+3fc (+0x7c)
[ [COLOR=Blue]6e 87 61 80[/COLOR]:38 0e e2 ee ]
80505854-80505857 4 bytes - nt!KiServiceTable+404 (+0x08)
[ [COLOR=Blue]9e 39 5d 80[/COLOR]:0a 0a e2 ee ]
805058a4-805058a7 4 bytes - nt!KiServiceTable+454 (+0x50)
[ [COLOR=Blue]94 53 5b 80[/COLOR]:a4 0f e1 ee ]
805a2cba-805a2cbd 4 bytes - nt!KeUserModeCallback+8
[ [COLOR=Blue]c2 9e f9 ff[/COLOR]:c8 45 87 6e ]
805b3e0f-805b3e15 7 bytes - nt!MmUnmapViewOfSection+17 (+0x11155)
[ [COLOR=Blue]cc cc cc cc cc 8b ff[/COLOR]:e9 bc bd 85 6e eb f9 ]
[B][COLOR=Blue]67[/COLOR][/B] errors : nt (805054e4-805b3e15)
上面使用!chkimg指令检查了nt模块的所有被修改的数据。(蓝色为原始值)经过分析,共有对nt!KiServiceTable的HOOK 14处,nt!KiServiceTable存储的就是SSDT地址,也就是SSDT共被HOOK了14处,与刚才咱们看到的一致。那
805a2cba-805a2cbd 4 bytes - nt!KeUserModeCallback+8
[ [COLOR=Blue]c2 9e f9 ff[/COLOR]:c8 45 87 6e ]
805b3e0f-805b3e15 7 bytes - nt!MmUnmapViewOfSection+17 (+0x11155)
[ [COLOR=Blue]cc cc cc cc cc 8b ff[/COLOR]:e9 bc bd 85 6e eb f9 ]这两个是神马?是inline hook!反汇编看一下0: kd>[COLOR=Red] u nt!KeUserModeCallback[/COLOR] nt!KeUserModeCallback: 805a2cb2 6a30 push 30h 805a2cb4 6800aa4d80 push offset nt!KiDebugRegisterContextOffsets+0x24 (804daa00) 805a2cb9 e8c845876e [COLOR=Blue] call QQProtect+0xb286 (eee17286)[/COLOR] 805a2cbe e8b9fbf5ff call nt!KiGetUserModeStackAddress (8050287c) 805a2cc3 8945e4 mov dword ptr [ebp-1Ch],eax 805a2cc6 8b18 mov ebx,dword ptr [eax] 805a2cc8 895dd8 mov dword ptr [ebp-28h],ebx 805a2ccb 8365fc00 and dword ptr [ebp-4],0 0: kd> [COLOR=Red]u 805b3e0f [/COLOR] nt!MmUnmapViewOfSection+0x17: 805b3e0f e9bcbd856e [COLOR=Blue] jmp QQProtect+0x3bd0 (eee0fbd0)[/COLOR] nt!NtUnmapViewOfSection: 805b3e14 ebf9 [COLOR=Blue] jmp nt!MmUnmapViewOfSection+0x17 (805b3e0f)[/COLOR] 805b3e16 55 push ebp 805b3e17 8bec mov ebp,esp 805b3e19 51 push ecx 805b3e1a 56 push esi 805b3e1b 64a124010000 mov eax,dword ptr fs:[00000124h] 805b3e21 8a8040010000 mov al,byte ptr [eax+140h]原来是HOOK了nt!KeUserModeCallback和nt!NtUnmapViewOfSection。咱们再看看他都HOOK了哪些SSDT
0: kd> [COLOR=Red]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;[/COLOR]
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> [COLOR=Red]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;[/COLOR]恢复inline hook
0: kd> [COLOR=Red]eb nt!KeUserModeCallback+8 c2 9e f9 ff; eb nt!MmUnmapViewOfSection+17 cc cc cc cc cc 8b ff;[/COLOR]看一下恢复的效果:
0: kd> [COLOR=Red]!chkimg -d nt[/COLOR] 0 errors : ntOk,恢复完毕
删除内核通知回调
咱们看看QQ有没有注册通知
0: kd>[COLOR=Red] r @$t0=poi([B]nt!PspCreateProcessNotifyRoutineCount[/B]);r @$t1=[B]nt!PspCreateProcessNotifyRoutine[/B];.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}[/COLOR]
e170209c f7710194 vmci!VMCI_DeviceGet+0x96e
0: kd> [COLOR=Red]r @$t0=poi(nt![B]PspCreateThreadNotifyRoutineCount[/B]);r @$t1=nt![B]PspCreateThreadNotifyRoutine[/B];.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}[/COLOR]
e1612504 eee15f48 QQProtect+0x9f48
0: kd>[COLOR=Red] r @$t0=poi(nt![B]PspLoadImageNotifyRoutineCount[/B]);r @$t1=nt![B]PspLoadImageNotifyRoutine[/B];.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}[/COLOR]
e16249e4 eee190d8 QQProtect+0xd0d8
0: kd>[COLOR=Red] r @$t0=poi(nt![B]CmpCallBackCount[/B]);r @$t1=nt![B]CmpCallBackVector[/B];.for(r @$t2=0; @$t2<@$t0; r @$t2=@$t2+1){dds (poi(@$t1+@$t2*4)^7)+4 l1;}[/COLOR]
e172be7c eee0fa40 QQProtect+0x3a40发现共注册了创建线程、加载模、注册表操作回调例程。现在把回调清除0: kd> [COLOR=Red]ed nt![B]PspCreateThreadNotifyRoutineCount [/B]0; ed nt![B]PspCreateThreadNotifyRoutine [/B]0;[/COLOR] 0: kd> [COLOR=Red]ed nt![B]PspLoadImageNotifyRoutineCount [/B]0; ed nt![B]PspLoadImageNotifyRoutine [/B]0;[/COLOR] 0: kd> [COLOR=Red]ed nt![B]CmpCallBackCount [/B]0; ed nt![B]CmpCallBackVector [/B]0;[/COLOR]Ok,这个时候,SSDT和inline hook恢复了 ,内核通知回调清除了,线程APC也设置了,继续执行就能终止线程。
0: kd> [COLOR=Red]g[/COLOR]过一会,在按Ctrl+break断下,检查一下线程是否已终止
0: kd>[COLOR=Red] 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)[B]0xeee0c000 [/B]&& (unsigned long)@$t5<(unsigned long)[B]0xeee36680[/B])){r @$t3;dt -b nt!_ETHREAD Cid. ExitStatus @$t3; dt -b nt!_KTHREAD Header. @$t3; }; \" [B]867b5830[/B]+@$t0"[/COLOR]
...
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=[COLOR=Red]86699130[/COLOR]
+0x1d0 ExitStatus : [COLOR=Red]0n1864 [/COLOR][COLOR=Blue]//0n1864=0x478[/COLOR]
+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 : [COLOR=Red]0n1 [/COLOR][COLOR=Blue]//已终止,信号状态变成有信号[/COLOR]
+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=[COLOR=Red]862de020[/COLOR]
+0x1d0 ExitStatus : [COLOR=Red]0n1864[/COLOR]
+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 : [COLOR=Red]0n1[/COLOR]
+0x008 WaitListHead : _LIST_ENTRY [ 0x8665ba50 - 0x8665ba50 ]
...
至此,此篇流水基本上结束了。差点忘了还有SSDTShadow没有看。恢复SSDTShadow hook
我们现在nt!NtWaitForSingleObject 函数上下个断点,断下一个win32线程,就可以看到win32k模块内存了
0: kd> [COLOR=Red]bp nt!NtWaitForSingleObject ".if(@@(((nt!_KTHREAD*)[B]@$thread[/B])->Win32Thread)!=0){;}.else{gc;}"[/COLOR]
breakpoint 0 redefined下完断点继续执行0: kd> [COLOR=Red]g[/COLOR]不一会就断下来了
nt!NtWaitForSingleObject: 805c16b6 6a2c push 2Ch看一下线程自己的ServiceTable字段
0: kd> [COLOR=Red]dd @@(((nt!_KTHREAD*)@$thread)->ServiceTable) l8[/COLOR] 8055d6c0 [COLOR=Red] 80505450 [/COLOR]00000000 [COLOR=Red]0000011c [/COLOR]805058c4 8055d6d0 [COLOR=Red] bf999b80 [/COLOR]00000000 [COLOR=Red]0000029b [/COLOR]bf99a890看SSDTShadow表
0: kd> [COLOR=Red]dds [B]bf999b80 [/B]l29b[/COLOR] ... bf99a160 bf8f48da win32k!NtUserFillWindow bf99a164 bf81b5fa win32k!NtUserFindExistingCursorIcon [B][COLOR=Blue][COLOR=Red]bf99a168 [/COLOR]eee124fe QQProtect+0x64fe[/COLOR][/B] bf99a16c bf91540e win32k!NtUserFlashWindowEx bf99a170 bf8e8688 win32k!NtUserGetAltTabInfo ...发现Hook了一个函数
检查一下win32k
0: kd> [COLOR=Red]!chkimg -d win32k[/COLOR] 0 errors : win32k竟然没有发现hook!为什么呢?经过分析(!dh命令),SSDTShadow表在win32k模块的.data段,SSDT在nt的.text段。看来!chkimg只对代码段做检查。
计算一下修改处地址的Rva
0: kd> [COLOR=Red]? bf99a168-bf800000[/COLOR] Evaluate expression: 1679720 = [COLOR=Blue]0019a168[/COLOR]Rva=0019a168
使用lordPE看一下0019a168处的值是多少

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