发布时间:2025-12-09 21:02:07 浏览次数:4
http://bbs.pediy.com/showthread.php?t=150284
这几天想了解下神秘的进程csrss,在学习和了解的过程中就写下了自己对csrss进程的理解。
Csrss(客户端/服务器运行时子系统)是 Win32 子系统的用户模式部分,在桌面管理、终端登录、控制台管理、错误报告报告和DOS 虚拟机等方面起着重要作用,另外还监控着系统内所有Win32 子系统进程和线程的运行,进程的创建与退出,都需要通知Csrss。
图1是用内核工具xuetr观察Csrss进程加载的模块。(以下的分析环境为xp sp2 32位)
图1
由图1中我们可以知道Csrss进程除了加载诸如Kernel32,ntdll等基础模块之外,basesrv.dll,csrsrv.dll ,winsrv.dll就是Csrss进程的核心模块,因此我们重点研究这几个模块,当然研究这几个模块就要那个这几个模块的pdb文件,我们利用windbg提供的工具SymChk 下载这几个模块的pdb文件,首先在cmd命令行中进入windbg目录,然后用如下命令:
Symchk /r c:\windows\system32 /s SRV*c:\mysymbols\*http://msdl.microsoft.com/download/symbols
如图2.
图2
这样我们就有了csrss进程核心模块的pdb文件,为下面我们用IDA分析这些模块提供了方便。
csrsrv.dll 里面有一个未导出符号叫做 CsrRootProcess, 对 csrss 起着重要的作用。CsrRootProcess 指向一个 CSR_PROCESS 结构。
CSR_PROCESS 结构 (Vista/2008, 对于 XP/2003 同样适用) 如下:
每一个进程都对应着一个 CSR_PROCESS 结构体,所有进程的CSR_PROCESS 结构体
通过成员struct _LIST_ENTRY ListLink 构成了一个链表,通过CsrRootProcess 可以遍历这个链表。当一个进程创建时,Csrss.exe 会新建一个CSR_PROCESS 结构体,加入到这个链表
中(从Csrss.dll中我们可以看到插入链表是通过未导出函数CsrInsertProcess实现的);当一个进程退出时,Csrss.exe 会将该进程对应的CSR_PROCESS 结构体从链表中删除(从Csrss.dll中我们可以看到插入链表是通过未导出函数CsrRemoveProcess实现的)。
下面我们可以简单看下这连个函数的实现:
Csrss 里面还存在着一个 关于线程的未导出符号叫做 CsrThreadHashTable。它是一个有 256 个元素的数组, 每一个元素都指向一个 CSR_THREAD 结构, 而 CSR_THREAD 结构同样包含一个 LIST_ENTRY。
typedef struct _CSR_THREAD{ // <size 0x38> union _LARGE_INTEGER CreateTime; struct _LIST_ENTRY Link; struct _LIST_ENTRY HashLinks; struct _CLIENT_ID ClientId; struct _CSR_PROCESS* Process; struct _CSR_WAIT_BLOCK* WaitBlock; void* ThreadHandle; unsigned long Flags; unsigned long ReferenceCount; unsigned long ImpersonateCount;} CSR_THREAD, *PCSR_THREAD;相关操作函数CsrInsertThread,CsrRemoveThread。
Win32 子系统进程与CSRSS 的通信
我们首先从csrss是怎么启动的作为入口点,用进程查看工具procexp.exe查看相关信息。
如下图3
图3
从图3中我们可以知道csrss进程的父进程是smss,我们同样利用procexp.exe工具查看csrss的启动参数是什么,如图4:
图4
从图4中我们首先能猜想到的就是ServerDll=winsrv:UserServerDllInitialization,3 的意思是winsev.dll这个核心服务dll里面存在导出函数UserServerDllInitialization。那么我们就先从这个函数里面找找看有什么有用的线索没。
从UserServerDllInitialization代码片段中我们发现了两张表,如图5
我们在IDA中看看这两张表(UserServerApiDispatchTable 图6):
图6
UserServerApiDispatchTable 这张表保存了很多函数。而UserServerApiServerValidTable这张表保存的是UserServerApiDispatchTable 表里面相对应函数是否有效的标志。
同样的道理我们看看ConServerDllInitialization这个函数,我们也发现了类似的表。
如下图7:
所以winsrv.dll里面有两个重要的保存函数的表:ConsoleServerApiDispatchTable(控制台管理),UserServerApiDispatchTable(终端登录之类)。
最后我们发现basesrv.DLL也存在一张保存函数的表:BaseServerApiDispatchTable,
如下图8
图8
Csrsrv.dll存在CsrServerApiDispatchTable这张表。(图9)
图9
四个分发表序号如下:
CsrServerApiDispatchTable:0
BaseServerApiDispatchTable:1
ConsoleServerApiDispatchTable:2
UserServerApiDispatchTable:3
因此我们可以知道csrss.exe启动时候参数winsrv:UserServerDllInitialization,3 的意思了。
Win32 子系统进程与CSRSS 的通信是通过lpc port,在图3中我们可以看到个名为\Windows\ApiPort 的LPC 端口与名为\Windows\sbApiPort的LPC端口。那么在 Csrss 中,对ApiPort 端口所接收到的LPC 消息的处理,主要是由csrsrv.dll 中的
CsrApiRequestThread 函数完成。CsrApiRequestThread 函数调用NtReplyWaitReceivePort 接收
消息,根据消息的类型执行特定的操作。
CsrApiRequestThread函数里面有一while循环处理各种请求,ReceiveMsg参数的ReceiveMsg.h.u2.s2.Type字段保存了消息的类型,当请求为LPC_REQUEST:
LPC参数中某一字段:高16 位指定是哪个分发表,低16 位为分发表中函数的索引值
,定义如下:
#define CSR_APINUMBER_TO_SERVERDLLINDEX( ApiNumber ) \
((ULONG)((ULONG)(ApiNumber) >> 16)) //高16 位指定是哪个分发表
#define CSR_APINUMBER_TO_APITABLEINDEX( ApiNumber ) \
((ULONG)((USHORT)(ApiNumber))) //低16 位为分发表中函数的索引值
这样就可以通过分发表调用函数。
ApiNumber = ReceiveMsg.ApiNumber;ServerDllIndex = CSR_APINUMBER_TO_SERVERDLLINDEX( ApiNumber );LoadedServerDll = CsrLoadedServerDll[ ServerDllIndex ](*(LoadedServerDll->ApiDispatchTable[ ApiTableIndex ]))( &ReceiveMsg, &ReplyStatus );
转载于:https://www.cnblogs.com/himessage/archive/2012/12/27/2835286.html