首页
社区
课程
招聘
[转帖]如何调试Windows子系统的服务器进程(CSRSS.EXE)
发表于: 2011-10-9 03:17 13311

[转帖]如何调试Windows子系统的服务器进程(CSRSS.EXE)

2011-10-9 03:17
13311
信息来源:中国黑客联盟(551K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3y4F1K9r3q4U0K9$3g2J5j5$3S2#2i4K6u0W2j5$3!0E0i4@1g2r3i4@1u0o6i4K6R3&6

作者:张银奎

Windows环境子系统进程(CSRSS.EXE,简称CSRSS)是Windows子系统的服务器进程。尽管从NT4开始,窗口管理(包括屏幕输出、用户输入和消息传递)和GDI的主体实现移入到内核(win32k.sys)中,但CSRSS仍然是Windows子系统的灵魂,它监管着系统内运行着的所有Windows进程和线程,每个进程在创建后都要到它这里注册登记后方能运行,退出时也要到此报告注销。除了掌管着各个进程的“生死存亡”,CSRSS在桌面管理、终端登录、控制台管理、HardError报告、和DOS虚拟机等方面也起着重要作用。总之,把CSRSS称为Windows系统的“大内总管”不算为过。CSRSS身担如此多的重任,所以Windows系统真的离不开它,如果尝试强行杀死CSRSS进程(使用kill或其它工具),那么系统便会以蓝屏(BSOD,Blue Screen Of Death)结束(图1),


图1 CSRSSS意外退出会导致系统蓝屏终止(BSOD)

从图1中可以看到,蓝屏的停止码(StopCode)为F4,好时髦的代号,不过在这里的含义是CRITICAL_OBJECT_TERMINATION——“生死攸关的”系统对象被终止!停止码后面括号中是BSOD的参数,第一个参数‘3’代表是被终止的是进程对象,第二个参数是该进程的EPROCESS结构指针,第三个参数指向的是进程的映像名称,第四个参数指向的是包含解释信息的字符串。使用WinDbg进行内核调试或分析DUMP文件可以观察这些信息。

尽管CSRSS在Windows系统中有着如此重要的地位,但是关于它的介绍却如凤毛麟角,而且大多也只是含糊其词不关痛痒的寥寥几句。就连著名的Windows内幕一书对它的介绍也如蜻蜓点水。微软的文档中也从未见过关于CSRSS原理和设计的正式介绍,在MSDN中搜索一下,找到的大多都是与CSRSS有关的系统故障信息。因此,可以说CSRSS是Windows世界中被“保密”的最好的模块之一。这种神秘性使得很多Windows领域的老将也不大熟悉CSRSS这个名字,更不知道它长什么样,有时竟还以为它是病毒呢。不过不管你是否熟悉或认识它,CSRSS始终在每一个Windows系统(不包括3.X或更早的Windows)中运行着,而且起着不可替代的作用。

那么是不是真的没有必要了解CSRSS呢?NO,因为其地位的重要性,要了解某些Windows机制(如进程管理、控制台窗口、用户态调试、HardError等),探索它又变得无法回避。

没有文档,没有代码,如何了解CSRSS呢?对于这样的难题,调试跟踪无疑是攻破这个神秘堡垒的最佳利刃,然而这也不是件容易的事。

首先,因为CSRSS是Windows系统的关键进程,关乎系统安全之大计,所以缺省状态下,系统是禁止CSRSS被调试的。另外对于Windows 2000或之前的版本,CSRSS是调试子系统的一部分,所以如果不启动特别选项,调试对话也是无法建立和工作的。

第二,不要以为这个大内总管很悠闲,在一个典型的Windows XP系统中,CSRSS通常有十几个工作线程在打理着系统的日常工作,虽然它们总共占用的CPU资源通常也就在1~2%左右,使用pstat观察一下(表1),你会发现它们大多都是在忙碌着的,模式切换次数很高,说明它们频繁奔走在内核态和用户态之间。更严重的是,当我们在调试器调试CSRSS时,那么中断到调试器必然导致CSRSS进程被挂起(Windows系统还不允许以线程为单位建立调试会话),那么它的所有线程也就被挂起了,于是整个Windows便如凝固了一样,我们也就无法继续在上面做任何事了。

表1 使用PSTAT工具观察CSRSS的各个线程状态

Pid(进城ID):4cc pri(优先级):13 Hnd(句柄):  895 Pf(Page Fault,内存页交换次数): 104959 Ws(工作集):  10980K csrss.exe
tid*
 pri
 Ctx Swtch
 StrtAddr
 User Time
 Kernel Time
 State
 
4d4
 15
 1624
 75B6DCD6
 0:00:00.000
 0:00:00.040
 WaitpcReply
 
4d8
 13
 23414
 75B6DA56
 0:00:00.010
 0:00:03.965
 Wait:UserRequest
 
4dc
 14
 243342
 75B441F0
 0:00:04.346
 0:00:02.343
 WaitpcReceive
 
4e0
 14
 3
 75B437E2
 0:00:00.000
 0:00:00.000
 WaitpcReceive
 
4ec
 14
 243133
 75B441F0
 0:00:04.516
 0:00:02.113
 WaitpcReceive
 
4f0
 15
 79992071
 75B69C8A
 0:00:00.000
 0:01:00.747
 Wait:UserRequest
 
4f4
 15
 8187110
 75B69C8A
 0:00:00.000
 0:01:03.130
 Wait:UserRequest
 
524
 14
 3
 75B69C8A
 0:00:00.000
 0:00:00.000
 Wait:UserRequest
 
3ec
 15
 1120152
 75B619D7
 0:00:01.381
 0:00:05.497
 Wait:UserRequest
 
63c
 15
 1
 75B693B6
 0:00:00.000
 0:00:00.000
 Wait:UserRequest
 
bac
 14
 241767
 75B441F0
 0:00:04.416
 0:00:02.353
 WaitpcReceive
 
ee8
 14
 349
 75B619D7
 0:00:00.090
 0:00:00.050
 Wait:UserRequest
 
E5c
 14
 212007
 75B441F0
 0:00:03.505
 0:00:01.912
 WaitpcReceive
 
ac4
 14
 165710
 75B441F0
 0:00:02.784
 0:00:02.353
 WaitpcReceive
 
404
 14
 166136
 75B441F0
 0:00:02.874
 0:00:01.922
 WaitpcReceive
 
cd8
 13
 165864
 75B441F0
 0:00:03.144
 0:00:01.552
 WaitpcReceive
 
908
 14
 166196
 75B441F0
 0:00:02.753
 0:00:01.422
 WaitpcReceive
 
e6c
 14
 14254
 75B441F0
 0:00:00.140
 0:00:00.050
 Wait:LpcReceive
 



* 各列的含义:tid,线程ID;pri,线程的优先级;Ctx Switch,模式切换;StrtAddr,起始地址;User Time,在用户态运行的时间;Kernel Time,在内核态运行的时间;State,线程状态。

解决第一个问题的方法是通过修改Windows的全局标志来启用对CSRSS的调试。全局标志是一个32位的整数,每一位对应一个选项。控制CSRSS调试的标志位叫***_ENABLE_CSRDEBUG,其所在位对应的16进制数是0x20000。通过以下两种方法之一可以设置***_ENABLE_CSRDEBUG标志(你应该有管理员权限):

直接修改注册表,将如下表键中的GlobalFlag(REG_DWORD)值,将其当前值与0x20000做或运算。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

使用Gflags工具,直接执行gflags /r +20000或者通过图形界面选中‘Enable debugging of Win32 Subsystem’(图2)。


需要重新启动系统以上改动才能生效。但由于我们后面的操作还要重新启动系统,所以大家可以统筹安排合适进行重新启动。

解决第二个问题的方法是使用两个Windows系统,即使用一个Windows系统来调试另一个Windows系统中的CSRSS。这需要分两步来做:

第一步,使用WinDbg工具在两个Windows系统间建立内核调试对话。WinDbg是微软公司的免费调试工具,可以从微软网站免费下载,链接如下:

d48K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3#2A6j5%4u0G2M7$3!0X3N6q4)9J5k6h3y4G2L8g2)9J5c8Y4N6Z5k6r3y4Q4x3V1k6V1k6i4k6@1L8$3!0D9M7#2)9J5c8X3c8W2j5Y4g2Y4k6$3W2F1k6#2)9J5c8X3c8W2k6X3q4#2L8s2c8Q4x3X3g2E0M7%4m8^5

两个Windows系统可以是物理上的两台PC上的两个Windows,或者使用一台电脑利用Virtual PC来安装第二个Windows系统。如果使用两台PC,那么可以使用串口线(Null Modem)或1394(FireWire)线建立连接。如果使用Virtual PC,那么可以利用管道模拟串口连接。对于前一种方法,WinDbg的帮助文件有非常详细的说明,不再赘述。对于如何使用Virtual PC或VMWare进行内核调试,请参考以下链接中的文章:

3a3K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4V1N6X3c8T1k6#2)9J5k6h3!0J5k6#2)9J5c8X3u0D9L8$3N6K6i4K6u0r3j5h3c8$3k6r3u0Y4i4K6g2X3M7%4W2K6N6r3g2E0i4K6u0r3j5i4u0@1K9h3y4D9k6i4y4Q4x3V1j5I4x3K6m8Q4x3X3g2S2M7%4m8^5

第二步,在被调试的Windows系统(debuggee)中,使用NTSD调试CSRSS。NTSD是WinDbg软件包中包含的另一个调试工具,它支持命令行下工作,还支持通过内核调试会话将输入和输出定向到另一台机器上,我们正是利用这一功能来在第二台Windows(debugger)中控制NTSD实现对CSRSS的调试。NTSD支持复杂的命令行选项,对于我们的用途,其命令行应该为:

ntsd -d -g -G -o -pd -p <CSRSS 进程ID>

-d表示将输入和输出(通过DbgPrint)定向到另一个Windows中的WinDbg中;-g表示忽略被调试进程的初始断点,如果不加此开关,那么将NTSD附加到CSRSS进程的第一个breakin断点会导致被调试系统僵死;-G表示忽略进程终止时的最后一个断点;-o表示调试被调试进程启动的子进程;-pd表示调试器应该自动与被调试进程分离。-p用来指定被调试进程的进程ID。注意WinDbg的帮助文件说CSRSS的进程ID总为-1,这对于Windows 2000和其后的Windows都不再成立。不过因为NTSD提供了一个简洁的方法,只要使用—选项就是调试CSRSS。所以在成功建立内核调试后,只要在被调试机上切换到WinDbg目录,然后输入NTSD --。

如果一切顺利,输入NTSD --后,在调试机上应该收到类似如下的信息,笔者使用的环境是两台机器安装的都是Windows XP(SP1),WinDbg的版本为6.6。

*** wait with pending attach



Executable search path is:

ModLoad: 4a680000 4a684000   C:\WINDOWS\system32\csrss.exe

ModLoad: 77f50000 77ff7000   C:\WINDOWS\System32\ntdll.dll

ModLoad: 75b40000 75b4a000   C:\WINDOWS\system32\CSRSRV.dll

ModLoad: 75b50000 75b5e000   C:\WINDOWS\system32\basesrv.dll

ModLoad: 75b60000 75ba6000   C:\WINDOWS\system32\winsrv.dll

ModLoad: 77d40000 77dcc000   C:\WINDOWS\system32\USER32.dll

ModLoad: 77e60000 77f46000   C:\WINDOWS\system32\KERNEL32.dll

ModLoad: 77c70000 77cb0000   C:\WINDOWS\system32\GDI32.dll

ModLoad: 77dd0000 77e5d000   C:\WINDOWS\system32\ADVAPI32.dll

ModLoad: 78000000 78086000   C:\WINDOWS\system32\RPCRT4.dll

ModLoad: 75e90000 75f37000   C:\WINDOWS\System32\sxs.dll

看到以上信息,说明NTSD已经成功附加(attach)到CSRSS进程了;NTSD也与调试机上的WinDbg成功建立了连接。上面的信息是CSRSS进程中的各个模块:NTDLL.DLL是所有Windows程序都离不开的系统支持库;CSRSRV.DLL、BASESRV.DLL和WINSRV.DLL是CSRSS进程用来真正实施系统服务的三个服务模块;USER32.DLL、KERNEL32.DLL、GDI32.DLL和ADVAPI32.DLL是Windows子系统模块,包含了大多数文档化了的Win32 API的入口。RPCRT4.DLL用来实现远程过程调用;SXS.DLL用来实现多个版本的Assembly (DLL)在系统内共存(Side-by-Side)。

接下来要解决的一个问题是,如何将CSRSS中断到调试器?对于普通的用户态或内核调试,这是个微不足道的问题,但是对于现在的特别环境,这却有点棘手。在调试机上试图中断,但这会中断到内核调试模式,不是我们希望的通过WinDbg来控制NTSD。因为NTSD在被调试机上没有界面,所以在被调试机上也无法控制命令它把CSRSS中断。WinDbg的帮助文件中说,类似这样的问题应该事先有计划好的方法在被调试进程中产生异常,这不太容易。最后一种方法是如果CSRSS有用户界面,那么先将其切换到前台,然后通过按通用的F12热键将其中断到调试器。

所以现在的问题是唤出CSRSS的界面,然后将其“擒获”到调试器。CSRSS有界面么?答案是肯定的。图2所示的窗口是CSRSS弹出的么?在冲击波病毒(微软安全公告号MS04-011)猖獗的那段日子(2004年4月)大家可能没少见到这个界面。弹出这个界面的方法很简单,只要强行杀掉LSASS(Local Security Authority Subsystem Service)进程就可以了。


图2 CSRSS进程显示的LSASS被杀界面

不过这个窗口不是CSRSS的界面。它是另一个重要的系统进程WinLogon的。而且利用这个界面的代价太大,当WinLogon弹出这个HardError时,系统已经启动了关机程序,此时我们已经没有多少时间做事情了。

图3是CSRSS显示的一种界面,当某个应用程序内出现未处理异常时,系统的缺省异常处理器(UnhandledExceptionFilter函数,位于KERNEL32.DLL)会通过调用NtRaiseHardError()这个未公开的系统服务将错误报告给系统,内核服务在通过LPC发给CSRSS,于是CSRSS显示出如下这个对话框。触发这个机制的方法就是随便写一个小程序,内部做个非法操作而且不加任何异常保护。


图3 CSRSS弹出的应用程序内出现未处理异常错误(Windows 2000)

当这个对话框出现时,按下F12可以成功将CSRSS中断到调试器。但是从Windows XP开始,CSRSS启动Dr. Watson(华生医生)来处理这类错误(界面如图4所示),所以这个方法只有在Windows 2000(或更早)中有效。


图4 在Windows XP中,CSRSS会启动Dr. Watson进程来显示HardError

CSRSS还有别的界面么,有。图5显示了CSRSS的另一种UI,大家一看便认得了。只要在用光盘安装一个软件的某一步时,取出光盘,然后再点继续便可以触发它了。为了不损坏光驱和光盘,建议大家使用Virtual PC做这个操作,只要点File文件菜单中的Install or Update Virtual Machine Additions启动安装,然后再选CD菜单中的Release CD。如果没安装Virtual PC的话,请大家谨慎操作,或者不要做这个实验了,后面我们会揭晓比这更简单的方法。


图5 CSRSS显示的缺盘通知

将图5所示的对话框切换到前台(有输入焦点),然后按下F12键。这时观察调试机中的WinDbg,如果出现类似图6所示的情形,那么便成功将CSRSS进程中断到调试器了。


图6 CSRSS被中断到调试器

下面仔细看一下图6所示的调试界面。首先注意,命令输入区与普通的内核调试和用户态调试都不同,Input提示符专门用于表示WinDbg在代理另一个调试器的输入。对于我们的情况,输入到这里的命令会送给另一台机器上的NTSD调试器。另一点要说明的是,检查图6中的进程ID(1d0),它应该与被调试机上的CSRSS进程的进程ID是一致的。

现在可以尝试输入一些简单的命令了,比如lm列出进程内的模块(结果与前面的清单类似),输入~*可以列出CSRSS进程内的所有线程。

但是做到这一步,还有一个问题要解决,这就是调试符号。注意图6中有一行ERROR信息,这是WinDbg在提醒我们没有找到合适的调试符号。

输入k命令显示函数调用栈,也会得到与此相关的警告:

ChildEBP RetAddr  

WARNING: Stack unwind information not available. Following frames may be wrong.

0120fed0 75b44443 ntdll!DbgBreakPoint

0120fff4 00000000 CSRSRV!CsrValidateMessageString+0x3d3

如何设置调试符号呢?对于普通的情况,WinDbg的文档中有比较详细的介绍。但我们现在要做的是在WinDbg的代理输入中为NTSD设置调试符号,WinDbg的文档中没有关于此的介绍。尝试通过!symfix命令自动设置微软的调试符号服务器,会得到如下错误:

0:010> !symfix

!symfix

Network paths are disallowed, symbol server is not available.

Set your symbol path to a symbol tree on the local machine.

试图设置从局域网上映射的驱动器,也会得到这种“不允许网络路径”的错误警告。看来只能在本地安装符号文件了。可以选择从微软的网站上免费下载你所需版本的调试符号,也可以从其它机器上复制过来(只需要复制前面列出的那些模块的符号文件就可以了,注意保持目录结构不变)。笔者的做法是从另一台机器上将WINSRV、CSRSS、CSRSRV等文件的调试符号复制到被调试机的C:\Symbols目录下。

准备好调试符号文件后,需要将其根目录设置给NTSD,这只要在Input提示符后输入.sympath c:\symbols变可以了。然后需要通过.reload命令使其生效。

做好以上步骤后,再次输入k命令,会发现刚才的警告没有了,而且这次的函数调用序列与刚才的也大相径庭。而且这次才是正确的结果,因为我们按F12键,Windows子系统的内核部分接收到此热键,然后触发了CSRSS中的SrvActivateDebugger服务,SrvActivateDebugger检查到要调试就是自己,便调用DbgBreakPoint中断到调试器。可见正确设置调试符号对于调试的重要性,不小心就会被误导!

0:010> k

k

ChildEBP RetAddr  

0120fec4 75b7a179 ntdll!DbgBreakPoint

0120fed0 75b44443 winsrv!SrvActivateDebugger+0x1a

0120fff4 00000000 CSRSRV!CsrApiRequestThread+0x253

有了调试符号以后,可以观察到很多DLL中没有输出的内部函数,比如使用x winsrv!*可以列出WINSRV.DLL中的所有符号,其中包含了很多负责重要职责的变量和例程,廖举数例:

n         winsrv!SrvExitWindowsEx – Windows子系统退出,关机使用;

n         winsrv!ConsoleInputThread – 控制台窗口的输入线程,如果使用线程控制命令将其挂起~<线程序号> n(先使用~*列出所有线程,通常有2个这样的线程),那么控制台窗口就不响应了,移动也不能;

n         winsrv!UserHardError – 用来弹出HardError的函数,通常调用winsrv!UserHardErrorEx,再使用winsrv!MessageBoxTimeoutW真正弹出对话框;

n         winsrv!HandleMouseEvent – 处理控制台窗口的鼠标输入;

看到这里大家是否发现什么了呢?既然控制台窗口的输入直接与CSRSS进程相关,那么当控制台窗口在前台时,按F12热键能否将CSRSS进程中断到调试器呢?答案是肯定的,而且这是最方便的方法。

好了,今天就介绍这么多,其实本文介绍的方法(NTSD+WinDbg)除了可以用来调试CSRSS进程外,还可以用来调试WINLOGON进程(在开发GINA模块时非常有用)。

最后要说明的是,CSRSS代表的是Client/Server Runtime Server Subsystem,即客户/服务器(运行时)子系统,好大气的名字!如果在开篇就介绍这个缩写,很难理解它的含义。看了本文的介绍,大家或许可以揣摩出这个名字的一些用意:相对于Windows子系统中的所有普通进程来说,CSRSS进程是服务器进程,其它进程是客户;在CSRSS进程内部,WINSRV、CSRSRV和BASESRV是三个重要的服务模块,这些服务的调用者是客户;对于其它字系统,Windows字系统在Windows中也有着不可缺少的服务器身份……

[培训]科锐软件逆向54期预科班、正式班开始火爆招生报名啦!!!

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 688
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习啦.............写得很好,顶一个~
2011-10-9 09:08
0
雪    币: 89
活跃值: (53)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
这本书除非是做CPU 的 要么用不着。。
2011-10-9 10:10
0
雪    币: 416
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
csrss.exe 我記得是windows的GUI的重要程序,基本上不能調適,因為她只要delay,就會導致整個OS都會當掉。
2011-10-9 10:23
0
雪    币: 304
活跃值: (512)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
可以被内核调试器调试
2011-10-11 08:40
0
游客
登录 | 注册 方可回帖
返回