关键词:APC,蓝屏,QueueUserAPC,ZwQueueApcThread,漏洞
曾在看雪上看到一篇 【】Ring3下把Windows XP SP2 弄蓝了…… 的帖子。
出于好奇,改了段QueueUserAPC的Delphi的代码(见该贴的5楼),结果SP3下也蓝屏了:
也许是太简单了,苦等不见高手回复。正好在学漏洞分析这门课,就拿这个问题来练手了 。
最后分析发现,其实这个蓝屏原因很简单QueueUserAPC的调用上范了初级错误,APC插csrss后执行出错,导致csrss被OS中止掉了。
虽然简单,不过重要的是分析过程,所以在这里记录一下。分析中难免有错,高手飘过...
-------------------------------------------------------------------------------------------------
由于出问题的API是QueueUserAPC,于是尝试着用OllyDbg跟入检查原因。可以看到,QueueUserAPC实际是通过调用ntdll!ZwQueueApcThread实现的。不过ZwQueueApcThread没办法进一步跟进去:
代码:
7C92E23D > B8 B4000000 MOV EAX,0B4 ; ZwQueueApcThread 7C92E242 BA 0003FE7F MOV EDX,7FFE0300 7C92E247 FF12 CALL DWORD PTR DS:[EDX] ; ntdll.KiFastSystemCall 7C92E249 C2 1400 RETN 14 SS: 0012FF30 7C834174 返回到 kernel32.7C834174 来自 ntdll.ZwQueueApcThread 7C92EB8B > 8BD4 MOV EDX,ESP ; ntdll.KiFastSystemCall 7C92EB8D 0F34 SYSENTER
好在实际测试发现,并不是SYSENTER进入后就马上蓝屏,也就是说出问题的地方并不是ntdll.KiFastSystemCall中。
由于不会SoftICE,所以没法进一步跟踪,看来只能从DUMP文件入手。
在“启动和故障恢复”选项卡中,选择核心内存转储方式(图2),然后运行引起蓝屏的程序,让Windows输出蓝屏时的内存DUMP:

这样,蓝屏后就会将高端内存输出到C:\WINDOWS\MEMORY.DMP中。用WinDBG打开DMP文件,!analyze -v进行分析(详细分析方法见二楼),发现该蓝屏的产生是系统关键进程CSRSS被意外终止所导致的。据此可以初步断定,由于ZwQueueApcThread插入APC队列使csrss.exe被终止,而不是最早插入APC的SYSTEM和SMSS.EXE这两个进程。
看来不了解APC的原理是无法继续分析了,我们先来看看什么是APC。APC即异步过程调用,是(Asynchronous Procedure Call)的缩写。这里直接引用《谈谈对APC的一点理解》中的原话:
APC在Windows系统中应用相当广泛,实际上TerminateThread就是利用APC实现的。使用TerminateThread可以在任何时候结束指定线程。查阅微软支持库Q254956(http://support.microsoft.com/kb/254956/en-us/)可知,调用TerminateThread会在目标进程中产生一个APC队列,然后强制所有线程进入等待状态,用于执行APC队列中的ExitThread函数,从而结束目标进程。这是一种典型的APC应用,由于使用APC来执行退出进程操作,这就导致了被TerminateThread结束的线程不能回到自身的清理函数中,这也是APC的一个特性。[/url]
APC方式执行的回调函数,实际上是中断目标进程转入指定回调程序执行。这样的好处在于不会有新的线程产生,而且可以控制目标线程的执行流程。返回目标进程领空也比较简单,由于APC调用前已经把返回地址压入堆栈中,只需适时的执行一条ret指令便可以返回,而无需像挂钩子函数或者远程线程中,执行繁琐的清理操作。
这么一来就清楚了。一旦我们劫持csrss.exe中的某个线程,让他去执行一些非法操作,于是csrss.exe就壮烈牺牲了。
有人说了,将CallBack地址赋值LoadLibraryA也会引起蓝屏?首先我要说的是,表1中那段APC代码实际是错的,光调用一个QueueUserAPC函数就错了两个参数,也正是因为写得有问题才引起的目标程序访问违规:
是不是很熟悉,QueueUserAPC这个API调用其实和CreateRemoteThread很像,只不过其根本不同在于CreateRemoteThread要创建新的线程用于执行我们的目标代码,而QueueUserAPC不用。
-------------------------------------------------------------------------------------------------
另外说说网上一些关于APC注入的误区。APC注入参数1不一定非要是LoadLibraryA,其他API也是可行的(网上教程都用LoadLibrary是因为载入DLL实现起来比较简单而且通用)。APC的可以帮助我们执行指定位置的任意代码,只要把QueueUserAPC参数1的指针指向要执行的ShellCode,然后等待系统处理这个用户模式APC就行了。
当然,搬网上某些APC注入教程的原话,“有时你等得花儿都谢了APC还没有执行”。确实如此,只有当目标线程调用了SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx等API时,用户模式APC才能执行,这也是大多数线程插入APC后没有反应的原因。就目前来看,公认的方法是通过暴力注入explorer.exe的所有线程来达到目标效果。实际explorer中,通常都会有这样两个线程等着你去插入:
代码:
ntdll!RtlQueueWorkItem+0x2b5 ntdll!RtlDowncaseUnicodeString+0x75
