1.3 建立用户登录会话
Windows 内核在阶段1 初始化的最后,启动了一个用户模式进程——会话管理器子系统(smss)。Smss 进程是Windows 操作系统的关键组成部分,它尽管是一个用户模式进程,但有其特殊性:首先,它是可信的,这意味着它可以做一些其他用户进程无法做的事情,比如创建安全令牌;其次,它直接建立在Windows 内核的基础上,只使用Windows内核提供的系统服务,而不依赖于任何一个环境子系统。这不难理解,因为smss 启动时,Windows 子系统尚未启动,而且,启动Windows 子系统本身正是smss 的任务之一。
Smss 进程启动以后,继续完成系统引导过程。Smss 做的工作有相当一部分依赖于注册表中的设置。在内核的阶段1 初始化过程中,配置管理器已经初始化,但是直到smss进程启动起来,只有SYSTEM 储巢已被加载到内存中。加载其他的储巢也是smss 的职责,它通过调用系统服务NtInitializeRegistry 来初始化注册表。在NtInitializeRegistry 系统服务中,除了用户轮廓储巢以外的所有其他储巢,均会被加载到系统中,并且注册表树也将建立起来。实际上,加载这些储巢的工作由CmpInitializeHiveList 函数来完成,它仅仅加载6 个储巢(包括SYSTEM 储巢),正好是表2.6 中的前6 个储巢。
Smss 在注册表中的主键是HKLM\SYSTEM\CurrentControlSet\Control\Session Manager,用于指示smss 在系统初始化阶段做一些必要的工作。图2.17 显示了在Windows Server2003 SP1 系统中该键的一个典型设置。
按照Session Manager 键中的指示,smss 主线程完成以下事项:
运行在启动时执行的程序,这些程序由BootExecute 值指定。
执行启动时的文件删除或重命名任务,这由FileRenameOperations 子键来指定。
打开已知的DLL,并且在对象管理器名字空间的\KnownDlls 目录下创建相应的内存区对象,这些已知DLL 的列表位于KnownDLLs 子键中。已知DLL 是系统全局共享DLL,包括子系统DLL 等,各种应用程序通常都会用到这些DLL。
创建页面文件,页面文件列表由Memory Management 子键中的PagingFiles 值指定。
建立系统的全局环境变量,这些环境变量由Environment 键下的值指定。
加载Windows 子系统的内核模式模块( win32k.sys ) , 这是通过系统服务NtSetSystemInformation 来完成的,该函数也会调用win32k.sys 中的初始化例程(即入口函数)。子系统内核模块的文件路径由SubSytstems 子键的Kmode 值指定。关于Windows 子系统的初始化过程,请参考9.2 节。
启动Windows 子系统进程(csrss.exe)。子系统进程的命令行字符串由SubSystems子键的Windows 值指定。
代码摘自\private\sm\server\sminit.c
NTSTATUS
SmpLoadDataFromRegistry(
OUT PUNICODE_STRING InitialCommand
)
/*++
函数功能:
该函数为NT会话管理器从注册表中加载所有的可配置的数据
--*/
{
NTSTATUS Status;
PLIST_ENTRY Head, Next;
PSMP_REGISTRY_VALUE p;
PVOID OriginalEnvironment;
RtlInitUnicodeString( &SmpDebugKeyword, L"debug" );
RtlInitUnicodeString( &SmpASyncKeyword, L"async" );
RtlInitUnicodeString( &SmpAutoChkKeyword, L"autocheck" );
InitializeListHead( &SmpBootExecuteList );
InitializeListHead( &SmpPagingFileList );
InitializeListHead( &SmpDosDevicesList );
InitializeListHead( &SmpFileRenameList );
InitializeListHead( &SmpKnownDllsList );
InitializeListHead( &SmpExcludeKnownDllsList );
InitializeListHead( &SmpSubSystemList );
InitializeListHead( &SmpSubSystemsToLoad );
InitializeListHead( &SmpSubSystemsToDefer );
InitializeListHead( &SmpExecuteList ); //初始化相关数据列表
Status = RtlCreateEnvironment( TRUE, &SmpDefaultEnvironment ); //创建默认的环境块
if (!NT_SUCCESS( Status )) {
KdPrint(("SMSS: Unable to allocate default environment - Status == %X\n", Status ));
return( Status );
}
// In order to track growth in smpdefaultenvironment, make it sm's environment while doing the registry groveling and then restore it
OriginalEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
NtCurrentPeb()->ProcessParameters->Environment = SmpDefaultEnvironment;
Status = RtlQueryRegistryValues( RTL_REGISTRY_CONTROL,
L"Session Manager",
SmpRegistryConfigurationTable,
NULL,
NULL
);
SmpDefaultEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
NtCurrentPeb()->ProcessParameters->Environment = OriginalEnvironment;
if (!NT_SUCCESS( Status )) {
KdPrint(( "SMSS: RtlQueryRegistryValues failed - Status == %lx\n", Status ));
return( Status );
}
Status = SmpInitializeDosDevices(); //初始化DOS设备
if (!NT_SUCCESS( Status )) {
KdPrint(( "SMSS: Unable to initialize DosDevices configuration - Status == %lx\n", Status ));
return( Status );
}
Head = &SmpBootExecuteList;
while (!IsListEmpty( Head )) {
Next = RemoveHeadList( Head );
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
#ifdef SMP_SHOW_REGISTRY_DATA
DbgPrint( "SMSS: BootExecute( %wZ )\n", &p->Name );
#endif
SmpExecuteCommand( &p->Name, 0 );
RtlFreeHeap( RtlProcessHeap(), 0, p );
} //运行在启动时执行的程序,这些程序由BootExecute值来指定
SmpProcessFileRenames(); //执行启动时的文件删除或重命名任务,这由FileRenameOperations子键来指定
Status = SmpInitializeKnownDlls(); //初始化已知的dlls
if (!NT_SUCCESS( Status )) {
KdPrint(( "SMSS: Unable to initialize KnownDll configuration - Status == %lx\n", Status ));
return( Status );
}
// 处理页面文件列表.
Head = &SmpPagingFileList;
while (!IsListEmpty( Head )) {
Next = RemoveHeadList( Head );
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
#ifdef SMP_SHOW_REGISTRY_DATA
DbgPrint( "SMSS: PagingFile( %wZ )\n", &p->Name );
#endif
SmpAddPagingFile( &p->Name );
RtlFreeHeap( RtlProcessHeap(), 0, p );
}
// 创建页面文件
SmpCreatePagingFiles();
// 完成注册表初始化
NtInitializeRegistry(FALSE);
Status = SmpCreateDynamicEnvironmentVariables( );
if (!NT_SUCCESS( Status )) {
return Status;
}
// Translate the system partition information stored during IoInitSystem into a DOS path and store in Win32-standard location.
SmpTranslateSystemPartitionInformation();
Head = &SmpSubSystemList;
while (!IsListEmpty( Head )) {
Next = RemoveHeadList( Head );
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
if ( !_wcsicmp( p->Name.Buffer, L"Kmode" )) {
BOOLEAN TranslationStatus;
UNICODE_STRING FileName; //"Kmode"指定子系统内核模块的文件路径
TranslationStatus = RtlDosPathNameToNtPathName_U(
p->Value.Buffer,
&FileName,
NULL,
NULL
);
if ( TranslationStatus ) {
PVOID State;
Status = SmpAcquirePrivilege( SE_LOAD_DRIVER_PRIVILEGE, &State );
if (NT_SUCCESS( Status )) {
Status = NtSetSystemInformation(
SystemExtendServiceTableInformation,
(PVOID)&FileName,
sizeof(FileName)
);
RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer);
SmpReleasePrivilege( State );
if ( !NT_SUCCESS(Status) ) {
Status = STATUS_SUCCESS;
}
}
}
else {
Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
}
}
#ifdef SMP_SHOW_REGISTRY_DATA
DbgPrint( "SMSS: Unused SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value );
#endif
RtlFreeHeap( RtlProcessHeap(), 0, p );
}
Head = &SmpSubSystemsToLoad;
while (!IsListEmpty( Head )) {
Next = RemoveHeadList( Head );
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
#ifdef SMP_SHOW_REGISTRY_DATA
DbgPrint( "SMSS: Loaded SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value );
#endif
if (!_wcsicmp( p->Name.Buffer, L"debug" )) {
SmpExecuteCommand( &p->Value, SMP_SUBSYSTEM_FLAG | SMP_DEBUG_FLAG );
}
else {
SmpExecuteCommand( &p->Value, SMP_SUBSYSTEM_FLAG );
}
RtlFreeHeap( RtlProcessHeap(), 0, p );
}
Head = &SmpExecuteList;
if (!IsListEmpty( Head )) {
Next = Head->Blink;
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
RemoveEntryList( &p->Entry );
*InitialCommand = p->Name;
// This path is only taken when people want to run ntsd -p -1 winlogon
// This is nearly impossible to do in a race free manner.
// In some cases, we can get in a state where we can not properly fail a debug API.
// This is due to the subsystem switch that occurs when ntsd is invoked on csr. If csr is relatively idle, this does not occur.
// If it is active when you attach, then we can get into a potential race. The slimy fix is to do a 5 second delay
// if the command line is anything other that the default.
{
LARGE_INTEGER DelayTime;
DelayTime.QuadPart = Int32x32To64( 5000, -10000 );
NtDelayExecution(
FALSE,
&DelayTime
);
}
}
else {
RtlInitUnicodeString( InitialCommand, L"winlogon.exe" );
InitialCommandBuffer[ 0 ] = UNICODE_NULL;
LdrQueryImageFileExecutionOptions( InitialCommand,
L"Debugger",
REG_SZ,
InitialCommandBuffer,
sizeof( InitialCommandBuffer ),
NULL
);
if (InitialCommandBuffer[ 0 ] != UNICODE_NULL) {
wcscat( InitialCommandBuffer, L" " );
wcscat( InitialCommandBuffer, InitialCommand->Buffer );
RtlInitUnicodeString( InitialCommand, InitialCommandBuffer );
KdPrint(( "SMSS: InitialCommand == '%wZ'\n", InitialCommand ));
}
}
while (!IsListEmpty( Head )) {
Next = RemoveHeadList( Head );
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
#ifdef SMP_SHOW_REGISTRY_DATA
DbgPrint( "SMSS: Execute( %wZ )\n", &p->Name );
#endif
SmpExecuteCommand( &p->Name, 0 );
RtlFreeHeap( RtlProcessHeap(), 0, p );
}
#ifdef SMP_SHOW_REGISTRY_DATA
DbgPrint( "SMSS: InitialCommand( %wZ )\n", InitialCommand );
#endif
return( Status );
}
如果利用Microsoft 提供的符号信息来调试WRK 系统,则可以观察到以上这些行为均在smss 主模块(smss.exe)的SmpLoadDataFromRegistry 函数中完成。由此也可以看出,Windows 子系统作为会话(session)的一部分,它的实例由smss 来启动。Smss 除了依据注册表中的设置来完成必要的系统引导工作以外,还执行以下的步骤,以进一步提供多会话和本地登录服务:
创建LPC 端口对象(\SmApiPort),以接收“加载子系统”或“创建会话”的请求。
启动登录进程(winlogon.exe),登录进程将会承担起与用户登录有关的事项。
NTSTATUS
SmpInit(
OUT PUNICODE_STRING InitialCommand,
OUT PHANDLE WindowsSubSystem
)
{
NTSTATUS st;
OBJECT_ATTRIBUTES ObjA;
HANDLE SmpApiConnectionPort;
UNICODE_STRING Unicode;
NTSTATUS Status;
ULONG HardErrorMode;
SmBaseTag = RtlCreateTagHeap( RtlProcessHeap(),
0,
L"SMSS!",
L"INIT\0"
L"DBG\0"
L"SM\0"
);
// Make sure we specify hard error popups
HardErrorMode = 1;
NtSetInformationProcess( NtCurrentProcess(),
ProcessDefaultHardErrorMode,
(PVOID) &HardErrorMode,
sizeof( HardErrorMode )
);
RtlInitUnicodeString( &SmpSubsystemName, L"NT-Session Manager" );
RtlInitializeCriticalSection(&SmpKnownSubSysLock);
InitializeListHead(&SmpKnownSubSysHead);
RtlInitializeCriticalSection(&SmpSessionListLock);
InitializeListHead(&SmpSessionListHead);
SmpNextSessionId = 1;
SmpNextSessionIdScanMode = FALSE;
SmpDbgSsLoaded = FALSE;
// 初始化安全描述符以获取更多的特权
// (保护模式尚未从注册表中读取).
st = SmpCreateSecurityDescriptors( TRUE );
if ( !NT_SUCCESS(st) ) {
return(st);
}
InitializeListHead(&NativeProcessList);
SmpHeap = RtlProcessHeap();
RtlInitUnicodeString( &Unicode, L"\\SmApiPort" );
InitializeObjectAttributes( &ObjA, &Unicode, 0, NULL, SmpApiPortSecurityDescriptor);
st = NtCreatePort(
&SmpApiConnectionPort,
&ObjA,
sizeof(SBCONNECTINFO),
sizeof(SMMESSAGE_SIZE),
sizeof(SBAPIMSG) * 32
); //此处创建LPC端口对象
ASSERT( NT_SUCCESS(st) );
SmpDebugPort = SmpApiConnectionPort;
st = RtlCreateUserThread(
NtCurrentProcess(),
NULL,
FALSE,
0L,
0L,
0L,
SmpApiLoop,
(PVOID) SmpApiConnectionPort,
NULL,
NULL
);
ASSERT( NT_SUCCESS(st) );
st = RtlCreateUserThread(
NtCurrentProcess(),
NULL,
FALSE,
0L,
0L,
0L,
SmpApiLoop,
(PVOID) SmpApiConnectionPort,
NULL,
NULL
);
ASSERT( NT_SUCCESS(st) );
// Configure the system
Status = SmpLoadDataFromRegistry( InitialCommand );
if (NT_SUCCESS( Status )) {
*WindowsSubSystem = SmpWindowsSubSysProcess;
}
return( Status );
}
加载win32k.sys的代码位于WRK中base\ntos\ex\sysinfo.c
…
//省略部分代码
case SystemExtendServiceTableInformation:
{
ULONG_PTR EntryPoint;
UNICODE_STRING Image;
PVOID ImageBaseAddress;
PDRIVER_INITIALIZE InitRoutine;
PIMAGE_NT_HEADERS NtHeaders;
PVOID SectionPointer;
DRIVER_OBJECT Win32KDevice;
// 判断系统信息缓冲区长度是否正确
if (SystemInformationLength != sizeof(UNICODE_STRING)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
// 如果先前的模式不是内核模式,就验证是否能够加载win32k子系统
if (PreviousMode != KernelMode) {
// 检测是否为会话首进程
if (MmIsSessionLeaderProcess(PsGetCurrentProcess()) == FALSE) {
return STATUS_PRIVILEGE_NOT_HELD;
}
// 检测是否有权限加载驱动
if (SeSinglePrivilegeCheck(SeLoadDriverPrivilege, UserMode) == FALSE) {
return STATUS_PRIVILEGE_NOT_HELD;
}
// 检测win32k子系统是否被加载
try {
// 测试并读取unicode字符串描述符
ProbeAndReadUnicodeStringEx(&Image,(PUNICODE_STRING)SystemInformation);
// 检测unicode字符串的长度是否正确
if (Image.Length != WIN32K_PATH_SIZE) {
return STATUS_PRIVILEGE_NOT_HELD;
}
// 测试unicode字符串的缓冲区并检测路径名称
ProbeForReadSmallStructure(Image.Buffer,
WIN32K_PATH_SIZE,
sizeof(UCHAR));
if (memcmp(Image.Buffer, &Win32kFullPath[0], WIN32K_PATH_SIZE) != 0) {
return STATUS_PRIVILEGE_NOT_HELD;
}
// 初始化win32全路径名称的字符串描述符
Image.Buffer = &Win32kFullPath[0];
Image.MaximumLength = WIN32K_PATH_SIZE;
} except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
// 递归调用这个内核服务,迫使先前的模式进入内核
Status = ZwSetSystemInformation(SystemExtendServiceTableInformation,
(PVOID)&Image,
sizeof(Image));
return Status;
}
// 先前的模式即为内核-加载指定的驱动
Image = *(PUNICODE_STRING)SystemInformation;
Status = MmLoadSystemImage(&Image,
NULL,
NULL,
MM_LOAD_IMAGE_IN_SESSION,
&SectionPointer,
(PVOID *)&ImageBaseAddress);
if (NT_SUCCESS(Status) == FALSE) {
return Status;
}
// 获取映像基地址
NtHeaders = RtlImageNtHeader(ImageBaseAddress);
if (NtHeaders == NULL) {
MmUnloadSystemImage(SectionPointer);
return STATUS_INVALID_IMAGE_FORMAT;
}
// 调用win32k.sys的PE头中指定的入口函数EntryPoint
EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;
EntryPoint += (ULONG_PTR)ImageBaseAddress;
InitRoutine = (PDRIVER_INITIALIZE)EntryPoint;
RtlZeroMemory(&Win32KDevice, sizeof(Win32KDevice));
ASSERT(KeGetCurrentIrql() == 0);
Win32KDevice.DriverStart = (PVOID)ImageBaseAddress;
Status = (InitRoutine)(&Win32KDevice, NULL);
ASSERT(KeGetCurrentIrql() == 0);
// 不成功的话就卸载
if (NT_SUCCESS(Status) == FALSE) {
MmUnloadSystemImage(SectionPointer);
} else {
MmSessionSetUnloadAddress(&Win32KDevice);
}
}
break;
…
Smss 的主线程在完成了以上描述的初始化工作以后,将在csrss 进程和winlogon 进程的句柄上等待。一旦等待成功,则意味着这两个进程中至少有一个退出了,于是系统崩溃。Windows 操作系统依赖于这两个进程,所以,它们也是保持Windows 操作系统正常运行不可缺少的组成部分。
接下来引导过程转到了winlogon 进程。它的职责包括:
创建初始的窗口站(WinSta0),并且为该窗口站创建一个桌面线程和RIT(Raw InputThread)以便接收标准输入。
创建登录桌面和默认桌面。登录桌面只有winlogon 进程才可以访问,因而也称为winlogon 桌面;而默认桌面允许其他进程访问。因此,当登录桌面活动时,其他进程无法访问与该桌面关联的代码或数据。Windows 用这种方式来保护与口令相关的操作,以及锁定桌面或解除桌面锁定这样的安全操作。
启动服务控制管理器(SCM,Service Control Manager)进程(services.exe)。在启动Windows 服务的过程中,会有更多的窗口站被创建。SCM 进程加载所有“自动-启动”类型的服务和设备驱动程序。
启动本地安全权威子系统( lsass ) 进程。然后与它建立一个LPC 连接(LsaAuthenticationPort 端口),以便在登录、注销和口令操作过程中交换信息。
关于这里提到的窗口站、桌面、桌面线程和RIT ,它们是Windows 窗口管理中的重要组成部分,由Windows 子系统内核模块win32k.sys 实现。Winlogon 的登录是通过一种称为GINA(图形化识别和认证,Graphical Identification andAuthentication)的可扩展机制来完成的,例如,默认的GINA 为msgina,可显示标准的Windows 登录对话框,指示用户输入用户名和口令。Winlogon 对于Ctrl+Alt+Del 按键序列做了特殊的保护,以防止其他程序截取此按键序列。第三方厂商可以利用像指纹或虹膜这样的生物特征来识别用户,然后从一个加密的数据库中提取出他们的口令,这样就不需要用户再输入口令了。
当winlogon 通过GINA 获得了用户名和口令以后,它首先调用lsass 函数LsaLookupAuthenticationPackage 以获得一个认证包的句柄,然后调用LsaLogonUser 将登录信息传递给认证包。一旦认证包认证了当前用户,则winlogon 继续该用户的登录过程;否则认证失败。因此,认证过程是由lsass 来完成的。
在登录过程的最后,winlogon 检查注册表HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Userinit 的值,并创建一个进程来运行该值字符串。该值串的默认值为userinit.exe 程序的路径。Userinit 进程加载当前登录用户的轮廓,然后检查HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell 的值,并创建一个进程来运行该值字符串;如果该值不存在,则运行HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Shell 的值,其默认值为explorer.exe。然后,userinit 进程退出。由于当前登录会话的Shell 程序(explorer.exe)已经启动,因此用户可以在桌面上操作了。
至此,引导过程结束,用户登录到系统中,并可通过explorer.exe 程序的用户界面操作系统中的资源,例如文件系统中的目录和文件;也可以启动各种应用程序。下图显示了从会话管理器启动一直到Shell 程序启动之间发生的事项。
在系统引导过程中,有多个进程被创建,包括smss 、csrss 、winlogon 、SCM(services.exe)、lsass、userinit(登录完成后自动退出)、explorer 等。这些进程都是操作系统的一部分,而且大多数还是可信的。实际上,在系统启动以后,当用户开始在Shell程序中操作时,他们通常可以看到更多的进程,这其中有些进程是由SCM 启动并初始化的。在Windows 中,SCM 提供了在系统启动时启动进程的机制,这些进程被称为服务(service)进程,它们类似于UNIX 中的守护进程。例如,Web 服务器或者数据库服务器显然应该以这样的方式来启动,因为无论是否有人登录到系统中,它们都必须被启动。在每个服务进程中,它可以宿纳一个或多个Windows 服务(Windows service)。
Smss和Winlogon进程的初始化过程代码不完整,这里给出两个文件的idb,借助IDA的graph view可以很清晰地看出流程.
Smss:主要的函数有SmConnectToSm,SmpLoadDataFromRegistry以及SmpLoadSubSystemForMuSession
---------------------
作者:cosmoslife
来源:CSDN
原文:https://blog.csdn.net/cosmoslife/article/details/7855425
版权声明:本文为博主原创文章,转载请附上博文链接!