转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家拍砖!
一、从WinMain开始分析
(1) 获得系统debug信息
NtQuerySystemInformation用于返回指定的系统信息。
NTSTATUS WINAPI NtQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
如果是内核debugger使能或 NtCurrentPeb()->BeingDebugged为TRUE,那么设置设置异常处理函数并设置定时器。
SetUnhandledExceptionFilter( WinlogonUnhandledExceptionFilter );
KernelDebuggerPresent = TRUE ;
SetTimerQueueTimer( NULL,
WlpPeriodicBreak,
NULL,
60 * 1000,
60 * 1000,
FALSE );
ULONG
NTAPI
WlpPeriodicBreak(
PVOID Param,
BOOLEAN Timeout
)
{
LARGE_INTEGER ZeroExpiration;
HANDLE hToken;
DWORD ReturnLength;
TOKEN_STATISTICS TokenStats;
NTSTATUS Status;
ZeroExpiration.QuadPart = 0;
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&hToken
);
if (NT_SUCCESS( Status )) {
Status = NtQueryInformationToken (
hToken,
TokenStatistics,
&TokenStats,
sizeof( TOKEN_STATISTICS ),
&ReturnLength
);
if (NT_SUCCESS( Status )) {
if (RtlLargeIntegerEqualTo( TokenStats.ExpirationTime, ZeroExpiration )) {
DbgBreakPoint();
}
}
NtClose( hToken );
}
if (g_BreakinProcessId != 0) {
Breakin(g_BreakinProcessId);
}
return(0);
}
定时器的处理函数中去获得当前TOKEN的TokenStatistics信息,然后取出结果中的TokenStats.ExpirationTime与ZeroExpiration相比。
if (RtlLargeIntegerEqualTo( TokenStats.ExpirationTime, ZeroExpiration ))
(1、1)RtlLargeIntegerEqualTo函数说明:
RtlLargeIntegerEqualTo用于大数比较,看两数是否相等。
BOOLEAN
RtlLargeIntegerEqualTo(
IN LARGE_INTEGER Operand1,
IN LARGE_INTEGER Operand2
);
(1、2)DbgBreakPoint
它进入内核模式的调试器
VOID
NTAPI
DbgBreakPoint(
VOID
);
DbgBreakPoint是内核模式,它等价于DebugBreak。
(2)初始化debug支持和logging
InitDebugSupport();
void
InitDebugSupport(void)
{
LoadDebugParameters("WinlogonDebug");
LoadDebugParameters("Winlogon");
}
(3)提升winlogon进程优先级,让自己更重要
/
// Make ourselves more important
//
if (!SetProcessPriority())
{
ExitProcess( EXIT_INITIALIZATION_ERROR );
}
BOOL SetProcessPriority(
VOID
)
{
//
// Bump us up to the high priority class
//
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
DebugLog((DEB_ERROR, "Failed to raise it's own process priority, error = %d", GetLastError()));
return(FALSE);
}
//
// Set this thread to high priority since we'll be handling all input
//
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
DebugLog((DEB_ERROR, "Failed to raise main thread priority, error = %d", GetLastError()));
return(FALSE);
}
return(TRUE);
}
(3、1)SetPriorityClass API 说明
设置指定进程的优先级类,这个值和进程中的每个线程的优先级一起决定每个线程的基本优先级级别。
BOOL WINAPI SetPriorityClass(
__in HANDLE hProcess,
__in DWORD dwPriorityClass
);
(4) 映射TCPIP信息
UpdateTcpIpParameters只是做注册表操作。
//
// Map the TCPIP information
//
UpdateTcpIpParameters();
(5)初始化全局变量和环境
//
// Initialize the globals
//
if ( !InitializeGlobals(hInstance) )
{
ExitProcess( EXIT_INITIALIZATION_ERROR );
}
(5、1) 先判断是不是终端的服务器
判断方法:根据共享数据USER_SHARED_DATA中的SuiteMast位和TerminalServer位决定。
如果是终端服务器那么通过NtQueryInformationProcess获是sessionId。
g_IsTerminalServer = !!(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer));
if (g_IsTerminalServer) {
//
// Query Winlogon's Session Id
//
if (!NT_SUCCESS(NtQueryInformationProcess(
NtCurrentProcess(),
ProcessSessionInformation,
&SessionInfo,
sizeof(SessionInfo),
NULL
))) {
ASSERT(FALSE);
ExitProcess( EXIT_INITIALIZATION_ERROR );
}
g_SessionId = SessionInfo.SessionId;
} else {
//
// For Non TerminaServer SessionId is always 0
//
g_SessionId = 0;
}
如果是终端服务器,那么载入Winsta.dll并存储函数指针
if (g_IsTerminalServer) {
if (!InitializeMultiUserFunctionsPtrs()) {
ExitProcess( EXIT_INITIALIZATION_ERROR );
}
}
(5、2)向windows登记,这样我们能创建windowstation等。
RegisterLogonProcess无法找到它的定义 。
//
// Register with windows so we can create windowstation etc.
//
if (!RegisterLogonProcess(HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), TRUE)) {
DebugLog((DEB_ERROR, "could not register itself as logon process\n"));
return FALSE;
}
NtCurrentTeb()来获取TEB的地址和PEB的地址。
(5、3 )获得sid,这样它能被放在ACLs中。
主要通过三个API函数:
RtlLengthRequiredSid获得要求的SID长;
RtlInitializeSid初始化SID;
RtlSubAuthoritySid ;
//
// Get our sid so it can be put on object ACLs
//
SidLength = RtlLengthRequiredSid(1);
g_WinlogonSid = (PSID)Alloc(SidLength);
ASSERTMSG("Winlogon failed to allocate memory for system sid", g_WinlogonSid != NULL);
RtlInitializeSid(g_WinlogonSid, &SystemSidAuthority, 1);
*(RtlSubAuthoritySid(g_WinlogonSid, 0)) = SECURITY_LOCAL_SYSTEM_RID;
(5、4)获得安装信息
安装类型是从注册表中读出来的。
//
// Get setup information
//
g_uSetupType = CheckSetupType() ;
g_fExecuteSetup = (g_uSetupType == SETUPTYPE_FULL) ||
(g_uSetupType == SETUPTYPE_UPGRADE)
(5、5)获得电脑名字并设置环境变量,这样我们能在之后查看到它。
GetComputerName为系统API
// Get a copy of the computer name in *my* environment, so that we
// can look at it later.
//
if (GetComputerName (szComputerName, &dwComputerNameSize)) {
SetEnvironmentVariable(COMPUTERNAME_VARIABLE, (LPTSTR) szComputerName);
}
(5、6)
//
// Set the default USERPROFILE and ALLUSERSPROFILE locations
//
if (g_fExecuteSetup) {
DetermineProfilesLocation(((g_uSetupType == SETUPTYPE_FULL) ? TRUE : FALSE));
}
dwSize = ARRAYSIZE(szProfile);
if (GetDefaultUserProfileDirectory (szProfile, &dwSize)) {
SetEnvironmentVariable(USERPROFILE_VARIABLE, szProfile);
}
dwSize = ARRAYSIZE(szProfile);
if (GetAllUsersProfileDirectory (szProfile, &dwSize)) {
SetEnvironmentVariable(ALLUSERSPROFILE_VARIABLE, szProfile);
}
(6)检查分页文件
//
// Check the pagefile
//
if (!g_fExecuteSetup)
{
CreateTemporaryPageFile();
}
(6、1)删除系统目录中的temppf.sys文件。
(6、2)检查看是否我们有一个分页文件,如果没有提醒用户
先用NtQuerySystemInformation查询页文件信息和系统表现系统,以它们的结果来判断是否有页文件。
PfiStatus = NtQuerySystemInformation(
SystemPageFileInformation,
&pfi,
sizeof(pfi),
&ReturnLength
);
PiStatus = NtQuerySystemInformation(
SystemPerformanceInformation,
&PerfInfo,
sizeof(PerfInfo),
NULL
);
(6、3)如果没有页文件,或总共提交的限制是最小的,那么我们创建一个增加折分页文件并告诉用户去做一些事情。
(6、3、1)创建临时的系统分页,
(6、3、2)创建一个20mb的分页文件。
创建成功后, 就把文件移动到新创建的分页文件中。
st = NtCreatePagingFile(
(PUNICODE_STRING)&FileName,
&MinPagingFileSize,
&MaxPagingFileSize,
0
);
if (!NT_SUCCESS( st )) {
if ( FileSizeInMegabytes > 0 ) {
FileSizeInMegabytes -= 2;
MinPagingFileSize = RtlEnlargedIntegerMultiply(FileSizeInMegabytes,0x100000);
MaxPagingFileSize = MinPagingFileSize;
goto retry;
}
} else {
MoveFileExW(PagingFileName.Buffer,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
}
(7)初始化安全模块
//
// Initialize security
//
if (!InitializeSecurity ())
{
ExitProcess( EXIT_SECURITY_INIT_ERROR );
}
BOOL
InitializeSecurity(
VOID)
{
//
// Set up our module globals
//
InitializeSecurityGlobals();
//
// Initialize the removable medial module
//
RmvInitializeRemovableMediaSrvcs();
return TRUE;
}
(7、1)设置模块全局变量
初始化全局变量的内容(主要是通过RtlAllocateAndInitializeSid,指定不同的类型得到不同的SID:本地SID、admin SID、限制的SID)
VOID
InitializeSecurityGlobals(
VOID
)
{
NTSTATUS Status;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
//
// Initialize the local sid for later
//
Status = RtlAllocateAndInitializeSid(
&gLocalSidAuthority,
1,
SECURITY_LOCAL_RID,
0, 0, 0, 0, 0, 0, 0,
&gLocalSid
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to initialize local sid, status = 0x%lx", Status));
}
//
// Initialize the admin sid for later
//
Status = RtlAllocateAndInitializeSid(
&gSystemSidAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&gAdminSid
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to initialize admin alias sid, status = 0x%lx", Status));
}
Status = RtlAllocateAndInitializeSid(
& NtAuthority ,
1,
SECURITY_RESTRICTED_CODE_RID,
0, 0, 0, 0, 0, 0, 0,
&gRestrictedSid
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to initialize restricted sid, status = 0x%lx", Status));
}
}
(7、2)初始化可移除的中间模块
通过RtlAllocateAndInitializeSid,指定不同的类型(SECURITY_LOCAL_SYSTEM_RID、SECURITY_BUILTIN_DOMAIN_RID、SECURITY_BUILTIN_DOMAIN_RID)得到不同的SID。
BOOL
RmvInitializeRemovableMediaSrvcs(
VOID
)
{
NTSTATUS
Status;
SID_IDENTIFIER_AUTHORITY
NtAuthority = SECURITY_NT_AUTHORITY;
//
// See if we need to protect floppy and/or CDRom drives.
//
//
// 初始化需要知道的SID
//
Status = RtlAllocateAndInitializeSid( &NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&RmvpLocalSystemSid
);
if ( !NT_SUCCESS(Status) )
{
return FALSE ;
}
Status = RtlAllocateAndInitializeSid( &NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&RmvpAdministratorsSid
);
if ( !NT_SUCCESS(Status) )
{
return FALSE ;
}
Status = RtlAllocateAndInitializeSid( &NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_POWER_USERS,
0, 0, 0, 0, 0, 0,
&RmvpPowerUsersSid );
if ( !NT_SUCCESS(Status) )
{
return FALSE ;
}
RtlInitUnicodeString( &RmvpNtfs, L"\\ntfs" );
RtlInitUnicodeString( &RmvpFat, L"\\fat" );
RtlInitUnicodeString( &RmvpCdfs, L"\\cdfs" );
return TRUE ;
}
(8)创建主终端
//
// Create the primary terminal.
//
if (!CreatePrimaryTerminal())
{
DebugLog((DEB_TRACE_INIT, "CreatePrimaryTerminal failed\n"));
ExitProcess( EXIT_PRIMARY_TERMINAL_ERROR );
}
(8、1)为terminal分配空间,并初始化成员变量
//
// Check mark
//
pTerm->CheckMark = TERMINAL_CHECKMARK;
ZeroMemory(pTerm->Mappers, sizeof(WindowMapper) * MAX_WINDOW_MAPPERS);
pTerm->cActiveWindow = 0;
pTerm->PendingSasHead = 0;
pTerm->PendingSasTail = 0;
(8、2) 如果不是控制台模式,那么等待连接,并确保指向它的sessionId
gpfnWinStationWaitForConnect接口从winsta.dll获得
//
// Wait here for the connection
//
if ( !g_Console ) {
if ( !gpfnWinStationWaitForConnect() ) {
DebugLog((DEB_ERROR, "wait for connect failed\n"));
return(FALSE);
}
}
if (!g_Console) {
if (!NT_SUCCESS(SetWinlogonDeviceMap(g_SessionId))) {
ExitProcess( EXIT_DEVICE_MAP_ERROR );
}
}
/***************************************************************************\
* SetWinlogonDeviceMap
*
* For non-console winlogon's, make them point to the session specific
* DeviceMap.
*
* History:
* 09-November-1997 SalimC Created
\***************************************************************************/
NTSTATUS
SetWinlogonDeviceMap( ULONG SessionId)
{
NTSTATUS Status = STATUS_SUCCESS;
WCHAR szSessionString[MAX_SESSION_PATH];
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
HANDLE DosDevicesDirectory;
if (SessionId == 0) {
return STATUS_INVALID_PARAMETER;
}
swprintf(szSessionString,L"\\Sessions\\%ld\\DosDevices",SessionId);
RtlInitUnicodeString( &UnicodeString, szSessionString );
InitializeObjectAttributes( &Obja,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenDirectoryObject( &DosDevicesDirectory,
DIRECTORY_ALL_ACCESS,
&Obja
);
if (!NT_SUCCESS( Status )) {
return Status;
}
//
// 设置Winlogon的 ProcessDeviceMap 到会话指定的DosDevices 目录
//
ProcessDeviceMapInfo.Set.DirectoryHandle = DosDevicesDirectory;
Status = NtSetInformationProcess( NtCurrentProcess(),
ProcessDeviceMap,
&ProcessDeviceMapInfo.Set,
sizeof( ProcessDeviceMapInfo.Set )
);
NtClose(DosDevicesDirectory);
return Status;
}
(8、3) 创建交互的window station
(8、3、1) 为WindowStation分配空间,并设置窗口名字为WinSta0
//
// Allocate space for a new WindowStation
//
pWS = LocalAlloc (LPTR, sizeof(WINDOWSTATION) +
(lstrlenW(WINDOW_STATION_NAME) + 1) * sizeof(WCHAR));
if (!pWS) {
DebugLog((DEB_ERROR, "Could not allocate windowstation structure\n"));
goto failCreateTerminal;
}
//
// Save the name
//
pWS->lpWinstaName = (LPWSTR)((LPBYTE) pWS + sizeof(WINDOWSTATION));
lstrcpyW (pWS->lpWinstaName, WINDOW_STATION_NAME);
(8、3、2) 用API CreateWindowStationW 创建窗口station ,并设置它为进程的window station
// Create the window station
//
pWS->hwinsta = CreateWindowStationW (WINDOW_STATION_NAME, 0, MAXIMUM_ALLOWED, NULL);
if (!pWS->hwinsta) {
DebugLog((DEB_ERROR, "Could not create the interactive windowstation\n"));
goto failCreateTerminal;
}
SetProcessWindowStation(pWS->hwinsta);
(8、3、3) 初始化winsta的安全性
InitializeWinstaSecurity完成初始化window station 安全性。
InitializeWinstaSecurity(pWS);
(8、3、3、1)创建Ace列表,目的是跟踪对winsta的访问
pList = CreateAceList( 16 );
if ( !pList )
{
return( FALSE );
}
pWS->Acl = pList;
(8、3、3、2) SetMyAce设置来定义winlogon的ACEs
eg:
SetMyAce(& ( pList->Aces[ pList->Active ++ ]),
g_WinlogonSid,
WINSTA_ALL,
NO_PROPAGATE_INHERIT_ACE
);
(8、3、3、3)AceListSetWinstaSecurity根据(8、3、3、2)定义的ACEs来设置winsta的安全性
这一特殊的安全描述符了:除非得到Winlogon的明确许可,否则没有其他的进程可以访问此窗口站。参考文章<<>>
BOOL
AceListSetWinstaSecurity(
PMYACELIST pList,
DWORD Count,
HWINSTA hWinsta )
{
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_INFORMATION si;
BOOL Result;
//
// Create the security descriptor
//
SecurityDescriptor = CreateSecurityDescriptor(pList->Aces, Count );
if (SecurityDescriptor == NULL) {
DebugLog((DEB_ERROR, "failed to create winsta security descriptor\n"));
return(FALSE);
}
//
// Set the DACL on the object
//
si = DACL_SECURITY_INFORMATION;
Result = SetUserObjectSecurity(hWinsta, &si, SecurityDescriptor);
//
// Free up the security descriptor
//
DeleteSecurityDescriptor(SecurityDescriptor);
//
// Return success status
//
if (!Result) {
DebugLog((DEB_ERROR, "failed to set windowstation security\n"));
}
return(Result);
}
(8、3、4) 创建winlogon的桌面及应用程序桌面
//
// Create winlogon's desktop
//
pWS->hdeskWinlogon = CreateDesktopW (WINLOGON_DESKTOP_NAME,
NULL, NULL, 0, MAXIMUM_ALLOWED, NULL);
if (!pWS->hdeskWinlogon) {
DebugLog((DEB_ERROR, "Could not create winlogon's desktop\n"));
goto failCreateTerminal;
}
//
// Create the application desktop
//
pWS->hdeskApplication = CreateDesktopW (APPLICATION_DESKTOP_NAME,
NULL, NULL, 0, MAXIMUM_ALLOWED, NULL);
if (!pWS->hdeskApplication) {
DebugLog((DEB_ERROR, "Could not create application's desktop\n"));
goto failCreateTerminal;
}
(8、3、5)设置winlogon桌面的安全性及应用程序桌面的安全性
方法与(8、3、3、2)(8、3、3、3)类似。
//
// Set desktop security (no user access yet)
//
if (!SetWinlogonDesktopSecurity(pWS->hdeskWinlogon, g_WinlogonSid)) {
DebugLog((DEB_ERROR, "Failed to set winlogon desktop security\n"));
}
if (!SetUserDesktopSecurity(pWS->hdeskApplication, NULL, g_WinlogonSid)) {
DebugLog((DEB_ERROR, "Failed to set application desktop security\n"));
}
(8、3、6)转换到winlogon的桌面
//
// Switch to the winlogon desktop
//
SetActiveDesktop(pTerm, Desktop_Winlogon);
BOOL
SetActiveDesktop(
PTERMINAL pTerm,
ActiveDesktops Desktop)
{
HDESK hDesk;
HDESK hPrevious;
DWORD LengthNeeded;
PWINDOWSTATION pWS = pTerm->pWinStaWinlogon;
if (Desktop == pWS->ActiveDesktop)
{
return(TRUE);
}
if (pWS->ActiveDesktop == Desktop_Application)
{
LockWindowStation(pWS->hwinsta);
pWS->hdeskPrevious = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
if (!GetUserObjectInformation( pWS->hdeskPrevious,
UOI_NAME,
pTerm->pszDesktop,
pTerm->DesktopLength,
&LengthNeeded) )
{
if (pTerm->DesktopLength != TYPICAL_STRING_LENGTH &&
pTerm->DesktopLength != 0)
{
LocalFree( pTerm->pszDesktop );
pTerm->pszDesktop = NULL;
pTerm->DesktopLength = 0;
}
pTerm->pszDesktop = LocalAlloc( LMEM_FIXED, LengthNeeded );
if (pTerm->pszDesktop)
{
pTerm->DesktopLength = LengthNeeded;
if (!GetUserObjectInformation( pWS->hdeskPrevious,
UOI_NAME,
pTerm->pszDesktop,
pTerm->DesktopLength,
&LengthNeeded))
{
pTerm->pszDesktop[0] = 0;
}
}
else
{
pTerm->DesktopLength = 0;
}
}
DebugLog((DEB_TRACE, "Source desktop was %ws\n", pTerm->pszDesktop));
}
switch (Desktop)
{
case Desktop_Winlogon:
hDesk = pWS->hdeskWinlogon;
break;
case Desktop_ScreenSaver:
hDesk = pWS->hdeskScreenSaver;
break;
case Desktop_Application:
if (pWS->hdeskPrevious)
{
hDesk = pWS->hdeskPrevious;
}
else
{
hDesk = pWS->hdeskApplication;
}
break;
default:
DebugLog((DEB_ERROR, "Error: Invalid desktop specified %d\n", Desktop));
return(FALSE);
}
if (SwitchDesktop(hDesk))
{
DebugLog((DEB_TRACE, "Switching desktop from %s to %s\n",
DbgGetDesktopName(pWS->ActiveDesktop),
DbgGetDesktopName(Desktop) ));
pWS->PreviousDesktop = pWS->ActiveDesktop;
pWS->ActiveDesktop = Desktop;
//
// If we're switching back to the user's desktop, then unlock the
// window station, so that the user can switch desktops again. Also,
// close our handle to the desktop. Note! Unlock before close, so
// that if this is the last handle to the desktop, cleanup can occur
// correctly.
//
if (pWS->ActiveDesktop == Desktop_Application)
{
UnlockWindowStation(pWS->hwinsta);
if (pWS->hdeskPrevious)
{
DebugLog((DEB_TRACE, "Closing handle %x to users desktop\n", pWS->hdeskPrevious));
CloseDesktop(pWS->hdeskPrevious);
pWS->hdeskPrevious = NULL;
}
}
return(TRUE);
}
DebugLog((DEB_WARN, "Could not switch desktop!\n"));
return(FALSE);
}
(8、3、7) 保存terminal
(9)从注册表中检查是不是安全模式
(10)设置线程到winlogon的桌面
SetThreadDesktop为系统API
//
// Set this thread to winlogon's desktop
//
SetThreadDesktop( pTerm->pWinStaWinlogon->hdeskWinlogon );
(11)改变用户到‘system‘
// Change user to 'system'
//
if (!SecurityChangeUser(pTerm, NULL, NULL, g_WinlogonSid, FALSE)) {
DebugLog((DEB_ERROR, "failed to set user to system\n"));
}
上面指定的UserLoggedOn为FALSE,在K、中也调用到SecurityChangeUser函数,它的UserLoggedOn为TRUE。
BOOL
SecurityChangeUser(
PTERMINAL pTerm,
HANDLE Token,
PQUOTA_LIMITS Quotas OPTIONAL,
PSID LogonSid,
BOOL UserLoggedOn
)
{
PWINDOWSTATION pWS = pTerm->pWinStaWinlogon;
LUID luidNone = { 0, 0 };
//
// Save the token for this windowstation
//
pWS->hToken = Token;
//
// Set appropriate protection on windows objects
//
if (UserLoggedOn || g_fExecuteSetup)
{
AddUserToWinsta( pWS,
LogonSid,
Token );
}
else
{
RemoveUserFromWinsta( pWS,
pWS->UserProcessData.UserToken );
}
SetUserDesktopSecurity(pWS->hdeskApplication,
LogonSid,
g_WinlogonSid);
//
// Setup new-process data
//
SetUserProcessData(&pWS->UserProcessData,
Token,
Quotas,
LogonSid,
g_WinlogonSid);
//
// Setup the appropriate new environment
//
if (UserLoggedOn) {
//
// Do nothing to the profile or environment. Environment and profiles
// are all handled in wlx.c:LogonAttempt and DoStartShell
//
pTerm->LogoffFlags = 0;
pTerm->TickCount = GetTickCount();
} else {
//
// Restore the system environment
//
CloseIniFileUserMapping(pTerm);
//
// ResetEnvironment if we are the console or
// we are not logging off. This prevents screen paints during logout
//
if ( g_Console || !pTerm->MuGlobals.fLogoffInProgress )
ResetEnvironment(pTerm);
SetWindowStationUser(pWS->hwinsta, &luidNone, NULL, 0);
}
//
// Store whether there is a real user logged on or not
//
pTerm->UserLoggedOn = UserLoggedOn;
return(TRUE);
}
(12)如果是控制台, 建造网络提供者事件(当网络列表改变时,此事件被触发)
//
// For the console winlogon, set the event that is toggled
// when the network provider list changes
//
if (g_Console) {
CreateNetworkProviderEvent();
}
(13) 初始化通知列表和JobControl
if ( !WlpInitializeNotifyList( pTerm ) ||
!InitializeJobControl() ||
!LogoffLockInit() )
{
ExitProcess( EXIT_NO_MEMORY );
}
(14) 启动系统进行
//
// Start the system processes
//
DebugLog((DEB_TRACE_INIT, "Execute system processes:\n"));
if (!ExecSystemProcesses())
{
DebugLog((DEB_TRACE_INIT, "ExecSystemProcesses failed\n"));
ExitProcess( EXIT_SYSTEM_PROCESS_ERROR );
}
(15)在我们登陆之前启动APP桌面线程
#ifndef _WIN64
StartAppDesktopThread(pTerm);
#endif // _WIN64
VOID StartAppDesktopThread(
PTERMINAL pTerm)
{
HANDLE hAppDesktopThread;
DWORD dwThreadId;
if (gbADTRunning) {
NDDETRACE(("NetddeAgentThread: aready running\n"));
return;
}
hAppDesktopThread = CreateThread(NULL, 0, ApplicationDesktopThread,
(LPVOID)pTerm, 0, &dwThreadId);
CloseHandle(hAppDesktopThread);
}
ApplicationDesktopThread线程中做以下事情:
(15、1)设置线程运行在WinSta0\Default
注意:在(10)是设置线程到winlogon的桌面。
SetThreadDesktop(pTerm->pWinStaWinlogon->hdeskApplication);
(15、2)初始化窗口数据并注册窗口类
初始化工作包括:注册窗口消息、获得IMM32.DLL模块的ImmDisableIme 接口 ,然后调用。
int NDDEAgntInit(
VOID)
int NDDEAgntInit(
VOID)
{
WNDCLASS WndClass;
wMsgNddeAgntExecRtn = RegisterWindowMessage(szAgentExecRtn);
wMsgNddeAgntWakeUp = RegisterWindowMessage(szAgentWakeUp);
wMsgNddeAgntAlive = RegisterWindowMessage(szAgentAlive);
wMsgNddeAgntDying = RegisterWindowMessage(szAgentDying);
{
WCHAR wszImmFile[MAX_PATH];
BOOL (WINAPI* fpImmDisableIme)(DWORD);
HMODULE hImm;
GetImmFileName(wszImmFile);
hImm = GetModuleHandleW(wszImmFile);
if (hImm) {
fpImmDisableIme = (BOOL (WINAPI*)(DWORD))GetProcAddress(hImm, "ImmDisableIme");
if (fpImmDisableIme) {
#ifndef LATER
fpImmDisableIme(0);
#else
fpImmDisableIme(-1);
#endif
}
}
}
ZeroMemory(&WndClass, sizeof(WndClass));
WndClass.lpszClassName = (LPTSTR)SZ_NDDEAGNT_CLASS;
WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
WndClass.hInstance = hInst;
WndClass.lpfnWndProc = NDDEAgntWndProc;
RegisterClass(&WndClass);
return TRUE;
}
(15、3) 创建窗口
g_hwndAppDesktopThread为 netdde 代理的主窗口。
/*
* now create the window
*/
g_hwndAppDesktopThread = CreateWindowEx(WS_EX_TOPMOST, gszAppName,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInst,
NULL);
(15、4) 清除任何未信任的共享。
/* set up lpszServer for NDDEAPI calls */
GetComputerName(szComputerName, &cbName);
_tcscpy(lpszServer, TEXT("\\\\"));
_tcscat(lpszServer, szComputerName);
/*
* clean up any trusted shares that have been modified or deleted
* since we last ran for this user
*/
if (IsServiceActive(&hSvcNetDDE, szNetDDE, FALSE)) {
CleanupTrustedShares();
}
(15、5) 分发窗口消息,进入消息循环
while (GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
(16) misc初始化,它需要在第一个terminal被创建时被调用。
//
// Finish some misc initialization. Note: This call can drop into setup
//
MiscInitialization(pTerm);
VOID MiscInitialization (PTERMINAL pTerm)
{
DWORD Win31MigrationFlags;
//
// Decide what to do about setup
//
if (g_fExecuteSetup)
{
//
// Init winmm
//
#if !defined(_WIN64_LOGON)
try
{
waveOutGetNumDevs();
}
except ( EXCEPTION_EXECUTE_HANDLER )
{
DebugLog(( DEB_ERROR, "Failed to initialize winmm, %x\n", GetExceptionCode() ));
}
#endif
//
// Run setup and reboot
//
ExecuteSetup(pTerm);
EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE);
NtShutdownSystem(ShutdownReboot);
}
else
{
//
// In the case of running setup in mini-setup mode,
// we want to be able to continue through and let the
// user logon.
//
if(g_uSetupType == SETUPTYPE_NOREBOOT) {
//
// go execute setup
//
ExecuteSetup(pTerm);
}
//
// Don't go any further if setup didn't complete fully. If this
// machine has not completed setup correctly, this will not return.
//
CheckForIncompleteSetup(g_pTerminals);
}
if (!IsWin9xUpgrade()) {
//
// Check to see if there is any WIN.INI or REG.DAT to migrate into
// Windows/NT registry.
//
// This code is skipped when the previous OS was Win9x.
//
Win31MigrationFlags = QueryWindows31FilesMigration( Win31SystemStartEvent );
if (Win31MigrationFlags != 0) {
SynchronizeWindows31FilesAndWindowsNTRegistry( Win31SystemStartEvent,
Win31MigrationFlags,
NULL,
NULL
);
InitSystemFontInfo();
}
}
#ifdef _X86_
//
// Do OS/2 Subsystem boot-time migration.
// Only applicable to x86 builds.
//
Os2MigrationProcedure();
#endif
//
// Load those pesky fonts:
//
hFontThread = StartLoadingFonts();
//
// Check if we need to run setup's GUI repair code
//
CheckForRepairRequest ();
}
(17)。
//
// Kick off a thread to watch the system dlls, initialize winmm, etc
//
if (g_Console) {
hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) InitializeSlowStuff,
NULL, 0, &dwThreadID);
if (hThread) {
SetThreadPriority (hThread, THREAD_PRIORITY_IDLE);
CloseHandle (hThread);
} else {
InitializeSlowStuff (NULL);
}
}
DWORD InitializeSlowStuff (LPVOID dummy)
{
//
// initialize the dll hell watcher
//
Sleep(100);
//
// call the winmm guys so they can start their message window
// and whatever else they need.
//
#if !defined(_WIN64_LOGON)
try
{
waveOutGetNumDevs();
}
except ( EXCEPTION_EXECUTE_HANDLER )
{
DebugLog(( DEB_ERROR, "Failed to initialize winmm, %x\n", GetExceptionCode() ));
}
#endif
if (SfcInitProt( SFC_REGISTRY_DEFAULT, SFC_DISABLE_NORMAL, SFC_SCAN_NORMAL, SFC_QUOTA_DEFAULT, NULL, NULL )) {
#if DBG
ExitProcess( EXIT_SYSTEM_PROCESS_ERROR );
#endif
}
return 0;
}
(18)载入GINA模块
(18、1)根据是是安全模式,如果是那么就直接从"%SystemRoot%\\system32\\msgina.dll"载入msgina.dll;
如果不是,那么从注册表GinaDLL字段中读取自定义DLL。
if ( pTerm->SafeMode )
{
ExpandEnvironmentStringsW(
TEXT("%SystemRoot%\\system32\\msgina.dll"),
pszGinaName,
MAX_PATH );
}
else
{
GetProfileString(
APPLICATION_NAME,
GINA_KEY,
TEXT("msgina.dll"),
pszGinaName,
MAX_PATH);
}
(18、2)
LoadGinaDll负责载入msgina.dll库,并获得其中的接口地址;
然后调用pWlxNegotiate
if (!LoadGinaDll (pTerm, pszGinaName)) {
DebugLog((DEB_TRACE_INIT, "Failed to load gina\n"));
ExitProcess( EXIT_GINA_ERROR );
}
(19)初始化SAS
//
// Initialize the secure attention sequence
//
if (!SASInit(pTerm))
{
DebugLog((DEB_TRACE_INIT, "Failed to create sas window\n"));
ExitProcess( EXIT_SAS_WINDOW_ERROR );
}
SASInit做了以下几件事:
(19、1)创建SAS窗口
pTerm->hwndSAS = CreateWindowEx(0L, szSASClass, TEXT("SAS window"),
WS_OVERLAPPEDWINDOW,
0, 0, 0, 0,
NULL, NULL, g_hInstance, NULL);
(19、2)把终端指针存在窗口数据中
//
// Store our terminal pointer in the window user data
//
SetWindowLongPtr(pTerm->hwndSAS, GWLP_USERDATA, (LONG_PTR)pTerm);
(19、3)把这个窗口设置为我们通知屏保和用户注销的窗口
//
// Register this window with windows so we get notified for
// screen-saver startup and user log-off
//
if (!SetLogonNotifyWindow(pTerm->hwndSAS)) {
DebugLog((DEB_ERROR, "Failed to set logon notify window"));
return FALSE;
}
(20)初始化GPO支持和自动回滚支持
InitializeGPOSupport( pTerm );
InitializeAutoEnrollmentSupport ();
WlAddInternalNotify(
PokeComCtl32,
WL_NOTIFY_LOGOFF,
FALSE,
FALSE,
TEXT("Reset ComCtl32"),
15 );
(21)进入主循环 (重点介绍)
此函数做的内容就是与GINA的交互。
//
// Main loop
//
MainLoop (pTerm);
(21、1) 初始化GINA DLL库
//
// Initialize the gina dll
//
if (!InitializeGinaDll(pTerm))
{
DebugLog((DEB_ERROR, "InitializeGinaDll failed\n"));
return;
}
BOOL
InitializeGinaDll(PTERMINAL pTerm)
{
#if DBG
if (TEST_FLAG(GinaBreakFlags, BREAK_INITIALIZE))
{
DebugLog((DEB_TRACE, "About to call WlxInitialize\n"));
DebugBreak();
}
#endif
//
// Perversely, this may not return. The GINA may in fact call SASNotify
// immediately, so update the state before we go in:
//
pTerm->WinlogonState = Winsta_NoOne;
DebugLog((DEB_TRACE_STATE, "InitGina: State is %d %s\n", Winsta_NoOne, GetState(Winsta_NoOne)));
if (!pTerm->Gina.pWlxInitialize(pTerm->pWinStaWinlogon->lpWinstaName,
pTerm,
NULL,
(PVOID) &WlxDispatchTable,
&pTerm->Gina.pGinaContext))
pWlxInitialize函数有五个参数:
A、winsta名字,[IN],由winlogon传GINA;
B、pTerm,[IN], PTERMINAL类型。 GINA调用分发表中的函数时,第一个参数为它。
C、NULL,[IN]
D、&WlxDispatchTable, 最重要的。[IN],GINA将调用从winlogon得到的分发表中的函数。
我们将在第二点中讲到。
WLX_DISPATCH_VERSION_1_3 WlxDispatchTable = {
WlxUseCtrlAltDel,
WlxSetContextPointer,
WlxSasNotify,
WlxSetTimeout,
WlxAssignShellProtection,
WlxMessageBox,
WlxDialogBox,
WlxDialogBoxParam,
WlxDialogBoxIndirect,
WlxDialogBoxIndirectParam,
WlxSwitchDesktopToUser,
WlxSwitchDesktopToWinlogon,
WlxChangePasswordNotify,
WlxGetSourceDesktop,
WlxSetReturnDesktop,
WlxCreateUserDesktop,
WlxChangePasswordNotifyEx,
WlxCloseUserDesktop,
WlxSetOption,
WlxGetOption,
WlxWin31Migrate,
WlxQueryClientCredentials,
WlxQueryInetConnectorCredentials,
WlxDisconnect,
WlxQueryTerminalServicesData
};
E、&pTerm->Gina.pGinaContext, [OUT],接收GINA返回的GINA类的类指针。
(21、2)如果是控制台,那么等待各种服务、MUP、DS启动
//
// Wait for various services to start
//
if (g_Console) {
WaitForServices(pTerm);
}
等待的服务有:"SamSs"、TEXT("NETLOGON")、TEXT("RpcSs")、WaitForMUP (120000);、WaitForDS (120000);
在等待过程中有窗口提示。
VOID WaitForServices(
PTERMINAL pTerm
)
{
HANDLE hDsReindexEvent ;
ULONG SamWaitTime = 15000 ;
if ( pTerm->SafeMode )
{
SamWaitTime = 120000 ;
}
StatusMessage(FALSE, 0, IDS_STATUS_SYSTEM_STARTUP );
WaitForServiceToStart( TEXT("SamSs"), SamWaitTime);
if ( pTerm->SafeMode )
{
//
// In safe mode, no sense waiting for services that
// are disabled to start.
//
return;
}
hDsReindexEvent = OpenEvent( SYNCHRONIZE,
FALSE,
L"NTDS.IndexRecreateEvent" );
if ( hDsReindexEvent ) {
StatusMessage( FALSE, 0, IDS_STATUS_DS_REINDEX );
WaitForSingleObject( hDsReindexEvent, INFINITE );
CloseHandle( hDsReindexEvent );
}
//
// Wait for the network to start
//
StatusMessage (FALSE, 0, IDS_STATUS_NET_START);
WaitForServiceToStart (SERVICE_NETLOGON, 120000);
StatusMessage (TRUE, 0, IDS_STATUS_RPCSS_START);
WaitForServiceToStart (TEXT("RpcSs"), 120000);
StatusMessage (TRUE, 0, IDS_STATUS_MUP_START);
WaitForMUP (120000);
StatusMessage (TRUE, 0, IDS_STATUS_DS_START);
WaitForDS (120000);
}
(21、3)启动profile映射API
//
// Start profile mapping APIs
//
if (g_Console) {
InitializeProfileMappingApi();
}
(21、4)做网络访问向导事务
//
// Do Net Access Wizard stuff
//
if ( CheckForNetAccessWizard(pTerm) )
{
// Reboot is required
pTerm->LastGinaRet = (DWORD) WLX_SAS_ACTION_SHUTDOWN_REBOOT;
return;
}
BOOL
CheckForNetAccessWizard (
PTERMINAL pTerm
)
{
BOOL fReboot = FALSE;
HKEY hkeyWinlogon = NULL;
LONG lResult;
DWORD dwSize, dwType;
ULONG uWizardType = NAW_NETID; // NAW_NETID is not valid
HINSTANCE hNetPlWiz;
LPNETACCESSWIZARD pfnNetAccessWiz;
DWORD dwWin9xUpgrade = 0;
//
// Do we have a post setup wizard entry? Or a Win9x upgrade entry?
//
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_KEY,
0, KEY_READ | KEY_WRITE, &hkeyWinlogon) == ERROR_SUCCESS) {
dwSize = sizeof(uWizardType);
RegQueryValueEx (
hkeyWinlogon,
L"RunNetAccessWizard",
NULL,
&dwType,
(LPBYTE) &uWizardType,
&dwSize
);
dwSize = sizeof(dwWin9xUpgrade);
RegQueryValueEx (
hkeyWinlogon,
L"SetWin9xUpgradePasswords",
NULL,
&dwType,
(LPBYTE) &dwWin9xUpgrade,
&dwSize
);
}
//
// check the wizard type, if its != NAW_NETID then we assume that we should
// be starting the wizard for post setup.
//
if ( uWizardType != NAW_NETID || dwWin9xUpgrade )
{
//
// Wait on the font loading thread here, so we don't inadvertantly re-enter
// the stuff in user that gets confused.
//
if ( hFontThread )
{
WaitForSingleObject( hFontThread, INFINITE );
CloseHandle( hFontThread );
hFontThread = NULL;
}
//
// wait for SAM to get its act together...
//
PAWaitForSamService();
//
// Get rid of the status UI so we don't overlap
//
RemoveStatusMessage(TRUE);
if ( !dwWin9xUpgrade ) {
//
// lets load the network account wizard and set it off
//
hNetPlWiz = LoadLibrary(L"netplwiz.dll");
if ( hNetPlWiz )
{
pfnNetAccessWiz = (LPNETACCESSWIZARD)GetProcAddress(hNetPlWiz, "NetAccessWizard");
if ( pfnNetAccessWiz )
(*pfnNetAccessWiz)(NULL, uWizardType, &fReboot);
FreeLibrary(hNetPlWiz);
//
// The wizard has completed, remove the value that
// caused it to run
//
RegDeleteValue (hkeyWinlogon, L"RunNetAccessWizard");
}
} else {
//
// Launch the Win9x upgrade password UI
//
SetWin9xUpgradePasswords (pTerm);
}
}
if ( hkeyWinlogon ) {
RegCloseKey (hkeyWinlogon);
}
return fReboot;
}
(21、5)
发送启动通知。
//
// Send the Startup notification
//
WlWalkNotifyList( pTerm, WL_NOTIFY_STARTUP );
WlWalkNotifyList会漫步通知列表,根据指定的标志调用操作。
VOID
WlWalkNotifyList(
PTERMINAL Terminal,
DWORD Operation
)
{
PLIST_ENTRY Scan ;
PWLP_NOTIFICATION_OBJECT Notify;
PWINDOWSTATION pWS = Terminal->pWinStaWinlogon ;
HANDLE ThreadHandle ;
DWORD tid ;
BOOL bAsync;
DWORD OperationMask ;
PWLP_NOTIFY_BOOTSTRAP Boot ;
WLP_NOTIFY_BOOTSTRAP LocalBoot ;
OperationMask = WLP_NOTIFY_FLAG( Operation );
LockNotifyList();
Scan = NotifyList.Flink ;
while ( Scan != &NotifyList )
{
Notify = CONTAINING_RECORD( Scan, WLP_NOTIFICATION_OBJECT, List );
if ( Notify->Type & OperationMask )
{
//
// Match. Now, check the options.
//
if ( (Notify->Dll == NULL) &&
((Notify->Type & WLP_NOTIFY_INTERNAL) == 0) )
{
//
// Not snapped yet. Snap it:
//
if ( !WlpSnapNotifyDll( Notify, OperationMask ) )
{
//
// Couldn't snap this one. Remove it and continue on:
//
Scan = Scan->Flink ;
RemoveEntryList( &Notify->List );
WlpDeleteNotificationObject( Notify );
continue;
}
}
Boot = (PWLP_NOTIFY_BOOTSTRAP) LocalAlloc( LMEM_FIXED,
sizeof( WLP_NOTIFY_BOOTSTRAP ) );
if ( Boot == NULL )
{
Scan = Scan->Flink ;
continue;
}
bAsync = ( Notify->Type & WLP_NOTIFY_ASYNCHRONOUS ) ? TRUE : FALSE;
if ( ( Operation == WL_NOTIFY_LOGOFF ) ||
( Operation == WL_NOTIFY_SHUTDOWN ) )
{
bAsync = FALSE;
}
Boot->Notify = Notify ;
Boot->Terminal = Terminal ;
Boot->Operation = Operation ;
ThreadHandle = CreateThread( 0, 0,
WlpExecuteNotify,
Boot,
0,
&tid );
if ( ThreadHandle )
{
if ( !bAsync )
{
DWORD WaitStatus ;
DWORD TotalWait = 0 ;
while ( TotalWait < Notify->dwMaxWait )
{
WaitStatus = WaitForSingleObject( ThreadHandle, 5000 );
if ( WaitStatus == STATUS_WAIT_0 )
{
break;
}
TotalWait += 5 ;
}
}
CloseHandle( ThreadHandle );
}
else
{
LocalFree( Boot );
}
}
Scan = Scan->Flink ;
}
UnlockNotifyList();
}
(21、6)进入循环
(21、6、1)
DealWithAutochkLogs();
DealWithAutochkLogs会枚举当前磁盘,并偿试着去处理它找到的任何的AUTOCHK logs。
do {
if ( GetDriveTypeW( pwszVolumeName ) == DRIVE_FIXED ) {
wcscpy( pwszBootExFile, pwszVolumeName );
wcscat( pwszBootExFile, L"bootex.log" );
if ( TransferAutochkLogToEventLogIfAvailable( pwszBootExFile )) {
DeleteFileW( pwszBootExFile );
}
}
} while ( FindNextVolume( h, pwszVolumeName, MAX_PATH + 1 ));
TransferAutochkLogToEventLogIfAvailable的原理是从找到的文件中读取事件列表,并通过API ReportEvent报错winlogon的事件。
(21、6、2)从注册表中获得verbose状态。
QueryVerboseStatus();
(21、6、3) 调用GINA的pWlxRemoveStatusMessage移除状态提示对话框。
RemoveStatusMessage(TRUE);
VOID
RemoveStatusMessage(
BOOL bForce
)
{
//
// Check if the gina is ready
//
if (!g_pTerminals || !g_pTerminals->Gina.pGinaContext) {
return;
}
//
// Remove the status message under these conditions:
//
// 1) We are not in verbose status message mode
// 2) We are in verbose status message mode and bForce is true
//
if (!g_bVerboseStatus || (g_bVerboseStatus && bForce)) {
g_pTerminals->Gina.pWlxRemoveStatusMessage(g_pTerminals->Gina.pGinaContext);
}
}
(21、6、4)如果Term的winlogon状态是Winsta_NoOne,那么
A、设置对话框的当前超时时间;
WlxSetTimeout(pTerm, TIMEOUT_NONE);
BOOL
WINAPI
WlxSetTimeout(
HANDLE hWlx,
DWORD Timeout
)
{
PTERMINAL pTerm;
if (pTerm = VerifyHandle(hWlx))
{
if ((pTerm->WinlogonState == Winsta_NoOne_Display) ||
(pTerm->WinlogonState == Winsta_Locked_Display) )
{
if (Timeout)
{
SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);
return(FALSE);
}
}
pTerm->Gina.cTimeout = Timeout;
TimeoutUpdateTopTimeout( Timeout );
return(TRUE);
}
SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR);
return(FALSE);
}
B、调用GINA的pWlxDisplaySASNotice接口来显示SAS通知;即:
C、如果我们获得用户logooff通知,那么意味着我们已经关机,一个远程的关机会发生,它已经在sysshut.c里启动。
//
if (pTerm->SasType == WLX_SAS_TYPE_USER_LOGOFF)
{
//
// We are *done*
//
DebugLog(( DEB_TRACE_STATE, "Received Logoff, setting state to %s\n",
GetState(Winsta_Shutdown) ));
break;
}
D、如果我们获得一个超时的SAS,那么
(21、6、5) 偿试登陆
WlxResult = LogonAttempt(pTerm);
A、为新的登陆创建一个登陆SID
pLogonSid = CreateLogonSid(NULL);
CreateLogonid做了以下事情:
首先,产生一个本地的唯一的ID;
Status = NtAllocateLocallyUniqueId(&Luid);
然后,为SID分配空间;
Length = RtlLengthRequiredSid(SECURITY_LOGON_IDS_RID_COUNT);
Sid = (PSID)Alloc(Length);
最后,将唯一的ID来填充SID空间。
RtlInitializeSid(Sid, &gSystemSidAuthority, SECURITY_LOGON_IDS_RID_COUNT);
ASSERT(SECURITY_LOGON_IDS_RID_COUNT == 3);
*(RtlSubAuthoritySid(Sid, 0)) = SECURITY_LOGON_IDS_RID;
*(RtlSubAuthoritySid(Sid, 1 )) = Luid.HighPart;
*(RtlSubAuthoritySid(Sid, 2 )) = Luid.LowPart;
B、如果不是控制台。
gpfnWinStationSetInformation是winsta.dll中的接口。
if (!g_Console) {
//
// BUGBUG Disabling winstations should be in terminal server and not in
// the registry. WinStationNotifyLogon() can return an error status noting this, and
// winlogon can call the GINA to put up the message.
//
if (GetProfileInt(APPLICATION_NAME, WINSTATIONS_DISABLED, 0) == 1) {
TimeoutDialogBoxParam(
pTerm,
NULL,
(LPTSTR)IDD_DISABLED,
NULL,
LogonDisabledDlgProc,
0,
TIMEOUT_NONE
);
WlxResult = WLX_SAS_ACTION_LOGOFF;
pTerm->WinlogonState = Winsta_NoOne;
DeleteLogonSid(pLogonSid);
return(WlxResult);
}
//
// Send a signal to the communications channel that
// we are going into Secure-Attention-Sequence mode.
//
gpfnWinStationSetInformation(
SERVERNAME_CURRENT,
LOGONID_CURRENT,
WinStationSecureDesktopEnter,
NULL,
0);
}
C、如果终端有log out,那么告诉gina忽略自动登陆。
//
// if this terminal has had a log out, then tell the gina to ignore auto logon
//
if ( pTerm->IgnoreAutoLogon )
Options |= WLX_OPTION_IGNORE_AUTO_LOGON;
D、设置登陆框的时间为120S。然后调用GINA的pWlxLoggedOutSAS函数。
WlxSetTimeout(pTerm, 120);
WlxResult = pTerm->Gina.pWlxLoggedOutSAS(pGinaContext,
pTerm->SasType,
&pWS->LogonId,
pLogonSid,
&Options,
&hToken,
&MprInfo,
&pProfileInfo );
E、
//
// Signal the communications channel that we are no longer
// in SAS mode.
//
if( !g_Console ) {
gpfnWinStationSetInformation(
SERVERNAME_CURRENT,
LOGONID_CURRENT,
WinStationSecureDesktopExit,
NULL,
0);
}
F、如果登陆成功
保存用户名,保存Domain;如果是终端服务器,那么调用MultiUserLogonAttempt。
if (WlxResult == WLX_SAS_ACTION_LOGON)
{
if (MprInfo.pszUserName)
{
pWS->UserName = AllocAndDuplicateString(MprInfo.pszUserName);
}
if ( MprInfo.pszDomain )
{
pWS->Domain = AllocAndDuplicateString( MprInfo.pszDomain );
}
if (g_IsTerminalServer)
{
WlxResult = MultiUserLogonAttempt(pTerm, &MprInfo, hToken);
}
}
MultiUserLogonAttempt过程介绍:
F、1 获得计算机名字
if (!GetComputerNameW(ComputerName, &Length)) {
F、2 如果GINA没有查询USERCONFIG数据,我们需要。
Error = QueryTerminalServicesDataWorker(pTerm, pMprInfo->pszUserName,
pMprInfo->pszDomain);
QueryTerminalServicesDataWorker做了以下事情:
F、2、1 获得计算机名
if (!GetComputerNameW(ComputerName, &Length))
F、2、2 获得DcNameBuffer。方法有两种:根据计算机名;根据API DsGetDcName获得Domain控制信息,然后从中抽取出来。
F、2、3 地入regapi.dll库,并获得接口RegUserConfigQuery、RegDefaultUserConfigQueryW,调用接口。
F、3 如果在检索USERCONFIG时发生错误或者远程登陆WinStations被禁用时,检查错误
if (Error != ERROR_SUCCESS || pTerm->MuGlobals.UserConfig.fLogonDisabled) {
if (Error != ERROR_SUCCESS) {
DebugLog((DEB_ERROR,"MultiUserLogonAttempt: Error from user "
"config query %u\n", Error));
}
if (pTerm->MuGlobals.UserConfig.fLogonDisabled) {
DebugLog((DEB_TRACE,"MultiUserLogonAttempt: fLogonDisabled\n"));
}
HandleFailedLogon(pTerm, NULL, (NTSTATUS)ERROR_CTX_LOGON_DISABLED, 0,
pMprInfo->pszUserName, pMprInfo->pszDomain);
return WLX_SAS_ACTION_LOGOFF;
}
F、4 发送domain、username和token给terminalServer
if (!gpfnWinStationNotifyLogon((BOOLEAN)TestTokenForAdmin(hToken), hToken, pMprInfo->pszDomain,
pMprInfo->pszUserName, L"", 0, &pTerm->MuGlobals.UserConfig))
F、5 发送服务名字、用户名、用户domain给客户端
gpfnWinStationSetInformation( SERVERNAME_CURRENT,
LOGONID_CURRENT,
WinStationClientData,
pInfo,
TotalSize );
F、6 如果不是控制台会话,那么处理WinStation回调
F、7 重新连接会话
WlxResult = WLX_SAS_ACTION_LOGON;
if (CtxConnectSession(pTerm)) {
WlxResult = WLX_SAS_ACTION_RECONNECTED;
}
else {
// Tell terminal service that a new terminal user session has been
// created.
if ((!g_Console) && (NULL != gpfnWinStationNotifyNewSession)) {
if (!gpfnWinStationNotifyNewSession(
SERVERNAME_CURRENT,
LOGONID_CURRENT))
{
HandleFailedLogon(pTerm, NULL, (NTSTATUS)GetLastError(), 0,
pMprInfo->pszUserName, pMprInfo->pszDomain);
WlxResult = WLX_SAS_ACTION_LOGOFF;
}
}
}
G、如果没有登陆成功
删除前面得到的SID
if (WlxResult != WLX_SAS_ACTION_LOGON)
{
DebugLog((DEB_TRACE_STATE, "LogonAttempt: Resetting state to %s\n", GetState(Winsta_NoOne)));
pTerm->WinlogonState = Winsta_NoOne;
DeleteLogonSid(pLogonSid);
if (pWS->UserName)
{
FreeAndNull(pWS->UserName);
}
if (pWS->Domain)
{
FreeAndNull(pWS->Domain);
}
return(WlxResult);
}
H、如果pWS->Domain存在,那么从注册表中读出用户名对应的键值是否存在,如果存在则
H、1 调用API ImpersonateLoggedOnUser .
if (lResult == ERROR_SUCCESS) {
//
// Ask the user if they want to merge their profiles
//
ImpersonateLoggedOnUser (hToken);
lResult = MergeProfiles (pTerm);
RevertToSelf();
if (lResult == ERROR_SUCCESS || lResult == ERROR_CONTINUE) {
//
// The profile was remapped or split into separate profiles
//
RegDeleteValue (hKey, pWS->UserName);
if (lResult != ERROR_CONTINUE) {
i = WLX_SAS_ACTION_NONE;
}
} else if (lResult == ERROR_REQUEST_ABORTED) {
//
// User cancelled the UI
//
// nothing needed
} else {
//
// An error occurred (code in lResult)
//
i = WLX_SAS_ACTION_LOGOFF;
}
}
ImpersonateLoggedOnUser它让调用线程模仿登陆用户的security context。
H、2 然后调用MergeProfiles来询问是否要合并他们的本地和domain配置。
如果要合并,那么:
把鼠标变为等待,然后又调用ImpersonateLoggedOnUser让调用线程模仿登陆用户的security context,并调用RemapAndMoveUser(找不到定义。)
如果返回值是不允许登陆,那么就做循环弹出对话框来判断,直到允许。
//
// User has chosen to merge their profile
//
hOldCursor = SetCursor (LoadCursor(NULL, IDC_WAIT));
ImpersonateLoggedOnUser (pWS->hToken);
b = RemapAndMoveUser (NULL, 0, LocalUser, DomainUser);
Error = GetLastError();
RevertToSelf();
SetCursor (hOldCursor);
if (!b && Error == ERROR_ACCESS_DENIED) {
do {
//
// Prompt for administrator credentials, then try again.
//
PwArgs.pTerm = pTerm;
rc = WlxDialogBoxParam(pTerm,
g_hInstance,
(LPTSTR)IDD_ADMIN_PW,
NULL,
AdminPwDlgProc,
(LPARAM) &PwArgs
);
if (rc != IDOK) {
return ERROR_CANCELLED;
}
hOldCursor = SetCursor (LoadCursor(NULL, IDC_WAIT));
ImpersonateLoggedOnUser (PwArgs.hToken);
b = RemapAndMoveUser (NULL, REMAP_PROFILE_KEEPLOCALACCOUNT, LocalUser, DomainUser);
Error = GetLastError();
RevertToSelf();
SetCursor (hOldCursor);
if (Error == ERROR_ACCESS_DENIED) {
//
// If non-admin credentials were entered, then try again
//
LoadString (NULL, IDS_REMAP_PROFILE_RETRY, StackMsgBuf, ARRAYSIZE(StackMsgBuf));
WlxMessageBox (pTerm, NULL, StackMsgBuf, Caption, MB_OK);
}
} while (Error == ERROR_ACCESS_DENIED);
LogoffIfError = TRUE;
}
H、3 根据H、2返回的结果进行注册表记录,并返回值。
I、如果有用户登陆成功,那么以下是有的:
MprLogonNotify通知logon的证书管理
//
// Okay, someone logged on. This, this is interesting.
//
MprLogonNotify(
pTerm,
NULL,
MprInfo.pszUserName,
MprInfo.pszDomain,
MprInfo.pszPassword,
MprInfo.pszOldPassword,
&pWS->LogonId,
&pWS->LogonScripts);
DestroyMprInfo(&MprInfo);
I、1 NoNeedToNotify根据注册表判断是否需要通知,如果不需要直接返回
if (NoNeedToNotify()) {
DebugLog((DEB_TRACE_MPR, "MprLogonNotify - skipping notification - only one provider\n"));
*MprLogonScripts = NULL;
return(DLG_SUCCESS);
}
I、2 RtlCreateEnvironment 创建通知数据的环境(RtlCreateEnvironment在MSDN中并没找到它的定义);
通过SetCommonNotifyVariables将窗口句柄、用户名、密码、Domain、LogonID放到通知数据的环境里。
RtlZeroMemory( &NotifyData, sizeof( NotifyData ) );
//
// Set up the environment variables that we will use to pass
// information to notify process
//
Status = RtlCreateEnvironment(
TRUE,
&NotifyData.Env );
if ( !NT_SUCCESS( Status ) )
{
return DLG_FAILURE ;
}
if (!SetCommonNotifyVariables(
&NotifyData.Env,
hwndOwner,
Name,
Domain,
Password,
OldPassword
)) {
return(DLG_FAILURE);
}
if (!SetLogonNotifyVariables(&NotifyData.Env, LogonId)) {
return(DLG_FAILURE);
}
SetCommonNotifyVariables里只调用了SetNotifyVariable,通过对SetNotifyVariable指定不同的类型把不同的数据加到环境数据中。
SetNotifyVariable就是调用了系统的API RtlSetEnvironmentVariable(它同样在MSDN中找不到):
BOOL
SetNotifyVariable(
PVOID * Env,
LPWSTR Variable,
LPWSTR Value
)
{
NTSTATUS Status ;
UNICODE_STRING Var ;
UNICODE_STRING Val ;
if ( !Variable )
{
return FALSE ;
}
if ( !Value )
{
return TRUE ;
}
RtlInitUnicodeString( &Var, Variable );
RtlInitUnicodeString( &Val, Value );
Status = RtlSetEnvironmentVariable(
Env,
&Var,
&Val);
return NT_SUCCESS( Status );
}
I、3 初始化我们的通知数据结构
NotifyData.pTerm = pTerm;
NotifyData.ReturnBuffer = NULL;
I、4 更新windowsstation 锁,这样我们的通知能被启动。
//
// Update windowstation lock so mpnotify can start.
//
if ( pTerm->pWinStaWinlogon->ActiveDesktop == Desktop_Winlogon )
{
UnlockWindowStation(pTerm->pWinStaWinlogon->hwinsta);
FastSetWinstaSecurity(pTerm->pWinStaWinlogon, FALSE);
}
I、5 创建对话框,这样它将初始化通知并等待完成
//
// Create the dialog that will initiate the notify and wait
// for it to complete
//
Result = WlxDialogBoxParam( pTerm,
g_hInstance,
(LPTSTR)IDD_CONTROL,
hwndOwner,
MprNotifyDlgProc,
(LPARAM)&NotifyData);
I、6 重新锁住windowstation
//
// Re-lock the windowstation.
//
if ( pTerm->pWinStaWinlogon->ActiveDesktop == Desktop_Winlogon )
{
LockWindowStation(pTerm->pWinStaWinlogon->hwinsta);
}
RtlDestroyEnvironment( NotifyData.Env );
if ( NotifyData.SAS != 0 )
{
DebugLog(( DEB_TRACE_MPR, "SAS interruption - reposting SAS\n" ));
EnableSasMessages( NULL, pTerm );
SASRouter( pTerm, (DWORD) NotifyData.SAS );
}
J、 等待字体载入线程
//
// Wait on the font loading thread here, so we don't inadvertantly re-enter
// the stuff in user that gets confused.
//
if ( hFontThread )
{
WaitForSingleObject( hFontThread, INFINITE );
CloseHandle( hFontThread );
hFontThread = NULL;
}
hFontThread为(16)中调用的StartLoadingFonts函数创建的载入字体的线程。
K、切换用户到已登陆的用户
SecurityChangeUser(pTerm, hToken, NULL, pLogonSid, TRUE);
L、如果从GINA的pWlxLoggedOutSAS返回的Options中有WLX_LOGON_OPT_NO_PROFILE属性,那么:
if (!TEST_FLAG(Options, WLX_LOGON_OPT_NO_PROFILE))
{
if (pProfileInfo) {
if (pProfileInfo->pszProfile)
{
pWS->UserProfile.ProfilePath =
AllocAndExpandEnvironmentStrings(pProfileInfo->pszProfile);
LocalFree(pProfileInfo->pszProfile);
}
else
{
pWS->UserProfile.ProfilePath = NULL;
}
if (pProfileInfo->dwType >= WLX_PROFILE_TYPE_V2_0) {
if (pProfileInfo->pszPolicy)
{
pWS->UserProfile.PolicyPath =
AllocAndDuplicateString(pProfileInfo->pszPolicy);
LocalFree(pProfileInfo->pszPolicy);
}
else
{
pWS->UserProfile.PolicyPath = NULL;
}
if (pProfileInfo->pszNetworkDefaultUserProfile)
{
pWS->UserProfile.NetworkDefaultUserProfile =
AllocAndDuplicateString(pProfileInfo->pszNetworkDefaultUserProfile);
LocalFree(pProfileInfo->pszNetworkDefaultUserProfile);
}
else
{
pWS->UserProfile.NetworkDefaultUserProfile = NULL;
}
if (pProfileInfo->pszServerName)
{
pWS->UserProfile.ServerName =
AllocAndDuplicateString(pProfileInfo->pszServerName);
LocalFree(pProfileInfo->pszServerName);
}
else
{
pWS->UserProfile.ServerName = NULL;
}
if (pProfileInfo->pszEnvironment)
{
pWS->UserProfile.Environment =
AllocAndDuplicateStrings(pProfileInfo->pszEnvironment);
LocalFree(pProfileInfo->pszEnvironment);
}
else
{
pWS->UserProfile.Environment = NULL;
}
} else {
pWS->UserProfile.PolicyPath = NULL;
pWS->UserProfile.NetworkDefaultUserProfile = NULL;
pWS->UserProfile.ServerName = NULL;
pWS->UserProfile.Environment = NULL;
}
}
DebugLog((DEB_TRACE_PROFILE, "Using initial profile path of %ws\n", pWS->UserProfile.ProfilePath));
LocalFree(pProfileInfo);
//
// Load profile, set environment variables, etc.
//
if (SetupUserEnvironment(pTerm))
{
OpenIniFileUserMapping(pTerm);
uh = ImpersonateUser(&pWS->UserProcessData, NULL);
SetWindowStationUser(pWS->hwinsta, &pWS->LogonId,
pWS->UserProcessData.UserSid,
RtlLengthSid(pWS->UserProcessData.UserSid));
StopImpersonating(uh);
//
// Update the window station lock so that apps can start.
//
UnlockWindowStation(pWS->hwinsta);
LockWindowStation(pWS->hwinsta);
//
// allocate floppies and CDRoms (if so configured)
//
RmvAllocateRemovableMedia( pLogonSid );
//
// Start a task for the network provider notify
//
if ( NetworkProviderEvent )
{
RegisterWaitForSingleObject(
&pWS->UserProfile.NetworkProviderTask,
NetworkProviderEvent,
NetworkProviderTask,
pTerm,
INFINITE,
0);
}
}
else
{
//
// Whoops, something went wrong. we *must* log the user
// out. We do this by passing LOGOFF back to mainloop.
//
WlxResult = WLX_SAS_ACTION_LOGOFF;
}
}
(21、6、6) 接下来就是根据LogonAttempt返回的结果做业务处理
A、如果是非控制台
if (!g_Console) {
if ( ((WlxResult == WLX_SAS_ACTION_RECONNECTED) || (WlxResult == WLX_SAS_ACTION_LOGOFF)) ) {
//
// LogonAttempt returns WLX_SAS_ACTION_RECONNECTED in the case that we
// reconnected to an existing session. In this case, we simply want
// to exit the while loop and then exit WinLogon.
// We also want to exit winlogon when a user hits Cancel on the Logon dialog.
// In this case LogonAttempt will return WLX_SAS_ACTION_LOGOFF
//
break;
} else if ((WlxResult == WLX_SAS_ACTION_NONE) && (pTerm->SasType == WLX_SAS_TYPE_CTRL_ALT_DEL) ) {
//
// If the Logon dialog is idle for 120 seconds (LOGON_TIMEOUT)
// exit the session.
//
DbgPrint("Timed out!!. Session %d will be terminated\n",NtCurrentPeb()->SessionId);
break;
}
}
B、如果是注销,那么调用DeleteRasConnections删除RAS连接
if ( WlxResult == WLX_SAS_ACTION_LOGOFF )
{
DeleteRasConnections(pTerm);
WlxResult = WLX_SAS_ACTION_NONE ;
}
BOOL
DeleteRasConnections(
PTERMINAL pTerm
)
{
HANDLE ImpersonationHandle;
SC_HANDLE hServiceMgr, hService;
SERVICE_STATUS status;
RASCONN rasconn;
LPRASCONN lprasconn = &rasconn;
DWORD i, dwErr, dwcb, dwc;
BOOL bRet;
PRASENUMCONNECTIONSW pRasEnumConnectionsW;
PRASHANGUPW pRasHangUpW;
BOOL FreeThatRasConn = FALSE;
PWINDOWSTATION pWS = pTerm->pWinStaWinlogon;
if ( GetProfileInt( WINLOGON, KEEP_RAS_AFTER_LOGOFF, 0 ) )
{
return( TRUE );
}
//
// Determine whether the rasman service is running.
//
hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if ( hServiceMgr == NULL )
{
DebugLog((DEB_ERROR, "Unable to object service controller, %d\n", GetLastError()));
return( FALSE );
}
hService = OpenService(
hServiceMgr,
RASMAN_SERVICE_NAME,
SERVICE_QUERY_STATUS);
if (hService == NULL)
{
CloseServiceHandle(hServiceMgr);
DebugLog((DEB_TRACE, "rasman not started, nothing to tear down\n"));
return( TRUE );
}
bRet = QueryServiceStatus( hService, &status );
CloseServiceHandle(hService);
CloseServiceHandle(hServiceMgr);
if (! bRet )
{
//
// Service in bad state, get out of here...
//
return( TRUE );
}
if (status.dwCurrentState != SERVICE_RUNNING)
{
//
// Service is not running
//
return( TRUE );
}
//
// Load the RASAPI DLL so we can make it do stuff
//
if ( !hRasApi )
{
hRasApi = LoadLibrary( RASAPI32 );
if ( !hRasApi )
{
return( FALSE );
}
}
pRasEnumConnectionsW = (PRASENUMCONNECTIONSW) GetProcAddress(
hRasApi,
"RasEnumConnectionsW" );
pRasHangUpW = (PRASHANGUPW) GetProcAddress(
hRasApi,
"RasHangUpW" );
if ( (!pRasEnumConnectionsW) ||
(!pRasHangUpW) )
{
return( FALSE );
}
//
// Impersonate the user
//
ImpersonationHandle = ImpersonateUser(&pWS->UserProcessData, NULL);
if (ImpersonationHandle == NULL) {
DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to impersonate user\n"));
return(FALSE);
}
//
// Enumerate the current RAS connections.
//
lprasconn->dwSize = sizeof (RASCONN);
dwcb = sizeof (RASCONN);
dwc = 1;
dwErr = pRasEnumConnectionsW(lprasconn, &dwcb, &dwc);
if (dwErr == ERROR_BUFFER_TOO_SMALL)
{
lprasconn = LocalAlloc(LPTR, dwcb);
FreeThatRasConn = TRUE;
if ( !lprasconn )
{
return( FALSE );
}
lprasconn->dwSize = sizeof (RASCONN);
dwErr = pRasEnumConnectionsW(lprasconn, &dwcb, &dwc);
if (dwErr)
{
if ( FreeThatRasConn )
{
LocalFree( lprasconn );
}
return( FALSE );
}
}
else if (dwErr)
{
return( FALSE );
}
//
// cycle through the connections, and kill them
//
for (i = 0; i < dwc; i++)
{
DebugLog((DEB_TRACE, "Hanging up connection to %ws\n",
lprasconn[i].szEntryName));
(VOID) pRasHangUpW( lprasconn[i].hrasconn );
}
if ( FreeThatRasConn )
{
LocalFree( lprasconn );
}
//
// Revert to being 'ourself'
//
if (!StopImpersonating(ImpersonationHandle)) {
DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to revert to self\n"));
}
return( TRUE );
}
B、1 如果是从ini文件中读出是保持RAS连接,那么直接返回;
B、2 打开“RASMAN“服务,通过获得服务句柄查询服务状态。
B、3 载入RASAPI32.DLL,获得RasEnumConnectionsW、RasHangUpW接口;
B、4 模拟用户模式;
ImpersonationHandle = ImpersonateUser(&pWS->UserProcessData, NULL);
B、5 调用B、3得到的接口枚举RAS连接,然后将RAS连接断开。
B、6 退出模拟模式;
if (!StopImpersonating(ImpersonationHandle)) {
DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to revert to self\n"));
}
C、如果是没有登陆成功,那么:
if (WlxResult == WLX_SAS_ACTION_NONE)
{
//
// If we got a user logoff notify, that means that WE HAVE ALREADY
// SHUT DOWN. A remote shutdown has taken place, and it has been
// started by sysshut.c.
//
if (pTerm->SasType == WLX_SAS_TYPE_USER_LOGOFF)
{
//
// We are *done*
//
DebugLog(( DEB_TRACE_STATE, "Got logoff during logon, setting to %s\n",
GetState(Winsta_Shutdown) ));
pTerm->WinlogonState = Winsta_Shutdown;
break;
}
//
// If we got a time out (meaning a screensaver timeout
// occurred during the logon prompt, then the prompt should be dead,
// but we'll hit here
//
if (pTerm->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
{
//
// run the screen saver
//
ScreenSaverResult = DoScreenSaver(pTerm, FALSE);
if (ScreenSaverResult < 0)
{
//
// This means that a SAS other than activity cancelled
// the screen saver, such as a GINA specific one.
// In this case, drop on through to the logonattempt,
// since the current sas is in pTerm.
//
NOTHING ;
}
else
{
if ( (ScreenSaverResult == 0) &&
(pTerm->SasType == WLX_SAS_TYPE_USER_LOGOFF) )
{
//
// Shutdown during the screen saver.
//
DebugLog(( DEB_TRACE_STATE, "Received Logoff during screensaver, setting state to %s\n",
GetState(Winsta_Shutdown) ));
pTerm->WinlogonState = Winsta_Shutdown;
break;
}
if ( ScreenSaverResult == WLX_SAS_ACTION_LOGOFF )
{
break;
}
}
//
// We're already at WlxResult == NONE, and State == NoOne,
// so we can just continue
//
}
//
// Make sure that we're back to NoOne:
//
pTerm->WinlogonState = Winsta_NoOne;
continue;
}
C、1 如果是退出
C、2 如果是超时,要求进入屏保
C、3 否则continue,继续偿试登陆。
D、如果RAS是睡眠或是关机
{
SuspendComputer((WlxResult == WLX_SAS_ACTION_SHUTDOWN_HIBERNATE) ? TRUE : FALSE,
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_SLEEP2) ? TRUE : FALSE);
pTerm->WinlogonState = Winsta_NoOne;
WlxResult = WLX_SAS_ACTION_NONE;
continue;
}
if (IsShutdown(WlxResult))
{
pTerm->LastGinaRet = WlxResult;
DebugLog((DEB_TRACE_STATE, "Setting state to %d (%s)\n",
Winsta_WaitForShutdown, GetState(Winsta_WaitForShutdown)));
pTerm->WinlogonState = Winsta_WaitForShutdown;
break;
}
E、如果是登陆成功
if (WlxResult == WLX_SAS_ACTION_LOGON)
{
#ifndef _WIN64
//
// Since someone logged on notify the nddeagnt thread that
// it can impersonate the logged on user
//
if (g_hwndAppDesktopThread != NULL) {
PostMessage(g_hwndAppDesktopThread,
WM_USERCHANGED,
(WPARAM)g_hwndAppDesktopThread,
(LPARAM)pTerm->pWinStaWinlogon->hToken);
}
#endif // _WIN64
WlWalkNotifyList( pTerm, WL_NOTIFY_LOGON );
if (DoStartShell(pTerm))
{
DebugLog(( DEB_TRACE_STATE, "Setting state to LoggedOnUser\n" ));
pTerm->WinlogonState = Winsta_LoggedOnUser ;
WlWalkNotifyList( pTerm, WL_NOTIFY_POSTSHELL );
//
// now wait for the user to take an action (like generating the SAS)
//
WlxResult = BlockWaitForUserAction(pTerm);
if (WlxResult == WLX_SAS_ACTION_FORCE_LOGOFF)
{
WaitForForceLogoff(NULL, pTerm);
WlxResult = WLX_SAS_ACTION_LOGOFF;
}
}
else
{
WlxResult = WLX_SAS_ACTION_LOGOFF;
}
}
E、1 给netdde 代理的主窗口发送WM_USERCHANGED消息,并把登陆成功的token发过去。
E、2 发送通知WL_NOTIFY_LOGON
WlWalkNotifyList会漫步通知列表,根据指定的标志调用操作。
E、3 启动terminate的Shell (重点)
DoStartShell 完成启动任务,如果成功,那么发送WL_NOTIFY_POSTSHELL通知,
if (DoStartShell(pTerm))
{
DebugLog(( DEB_TRACE_STATE, "Setting state to LoggedOnUser\n" ));
pTerm->WinlogonState = Winsta_LoggedOnUser ;
WlWalkNotifyList( pTerm, WL_NOTIFY_POSTSHELL );
//
// now wait for the user to take an action (like generating the SAS)
//
WlxResult = BlockWaitForUserAction(pTerm);
if (WlxResult == WLX_SAS_ACTION_FORCE_LOGOFF)
{
WaitForForceLogoff(NULL, pTerm);
WlxResult = WLX_SAS_ACTION_LOGOFF;
}
}
else
{
WlxResult = WLX_SAS_ACTION_LOGOFF;
}
BOOL
DoStartShell(
PTERMINAL pTerm
)
{
HANDLE hImp;
BOOL StartStatus ;
HINSTANCE hInstMPR;
PFNRESTORENETCON pfnRestoreNetCon;
PVOID pNewEnvironment;
WCHAR szDesktop[MAX_PATH];
PWINDOWSTATION pWS = pTerm->pWinStaWinlogon;
UINT ErrorMode ;
RemoveStatusMessage(FALSE);
(void) DisplayPreShellLogonMessages(pTerm);
//
// If not logging in as Guest, System or Administrator then check for
// migration of Windows 3.1 configuration inforation.
//
if (szAdminName[ 0 ] == TEXT('\0'))
{
LoadString(NULL, IDS_ADMIN_ACCOUNT_NAME, szAdminName, ARRAYSIZE(szAdminName));
}
if (g_Console) {
if (!IsUserAGuest(pWS) &&
_wcsicmp(pWS->UserName, szAdminName)
)
{
Windows31Migration(pTerm);
}
//
// Setup the user's power profile. For Hydra, default power settings for
// physical console's user take effect.
//
StatusMessage(TRUE, 0, IDS_STATUS_POWER_PROFILE);
SetPowerProfile(pTerm);
}
//
// Further initialize multimedia and play the user's logon sound
//
StatusMessage(TRUE, 0, IDS_STATUS_PLAY_LOGON_SOUND);
{
BOOL fBeep;
hImp = ImpersonateUser(&pWS->UserProcessData, NULL);
__try { WinmmLogon(g_Console); }
__except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; }
if (OpenIniFileUserMapping(pTerm))
{
__try
{
//
// Migrate driver settings
//
MigrateAllDrivers();
MigrateSoundEvents();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
NOTHING ;
}
//
// Whenever a user logs in, have WINMM.DLL check if there
// are any sound events within the [SOUNDS] section of
// CONTROL.INI that haven't been ported into HKCU/AppEvents.
// If there are, migrate those schemes to their new home.
// This must be done before the upcoming PlaySound() call,
// as PlaySound() uses the HKCU/AppEvents schemes-listing
// to resolve an SND_ALIAS_ID request.
//
if (!SystemParametersInfo(SPI_GETBEEP, 0, &fBeep, FALSE)) {
// Failed to get hold of beep setting. Should we be
// noisy or quiet? We have to choose one value...
fBeep = TRUE;
}
if (fBeep) {
PlaySoundAsync( pTerm, (LPCTSTR) SND_ALIAS_SYSTEMSTART );
//(*(g_PlaySound))((LPCSTR)SND_ALIAS_SYSTEMSTART,
// NULL,
// SND_ALIAS_ID | SND_ASYNC | SND_NODEFAULT);
}
CloseIniFileUserMapping(pTerm);
}
StopImpersonating(hImp);
}
StatusMessage(TRUE, 0, IDS_STATUS_RESTORE_NET);
//
// Restore the user's network connections
//
if ( pTerm->SafeMode == FALSE )
{
if (OpenHKeyCurrentUser(pTerm->pWinStaWinlogon))
{
if (OpenIniFileUserMapping(pTerm))
{
if (hImp = ImpersonateUser(&pWS->UserProcessData, NULL))
{
if (hInstMPR = LoadLibrary(TEXT("mpr.dll")))
{
pfnRestoreNetCon = (PFNRESTORENETCON)GetProcAddress (hInstMPR,
"WNetRestoreConnectionW");
if (pfnRestoreNetCon)
{
WCHAR szUserName[100];
if (pWS->UserName) {
szUserName[0] = TEXT('\0');
GetEnvironmentVariableW (USERNAME_VARIABLE, szUserName, 100);
SetEnvironmentVariableW (USERNAME_VARIABLE, pWS->UserName);
}
pfnRestoreNetCon(NULL, NULL);
if (pWS->UserName) {
if (szUserName[0] != TEXT('\0'))
SetEnvironmentVariableW (USERNAME_VARIABLE, szUserName);
else
SetEnvironmentVariableW (USERNAME_VARIABLE, NULL);
}
}
FreeLibrary (hInstMPR);
}
StopImpersonating(hImp);
}
CloseIniFileUserMapping(pTerm);
}
CloseHKeyCurrentUser(pTerm->pWinStaWinlogon);
}
}
//
// Notify the clients
//
WlWalkNotifyList( pTerm, WL_NOTIFY_STARTSHELL );
RemoveStatusMessage(TRUE);
WlxSetTimeout(pTerm, 120);
pNewEnvironment = CopyEnvironment(pWS->UserProcessData.pEnvironment);
wsprintfW (szDesktop, L"%s\\%s", pWS->lpWinstaName,
APPLICATION_DESKTOP_NAME);
#if DBG
if (TEST_FLAG(GinaBreakFlags, BREAK_ACTIVATE))
{
DebugLog((DEB_TRACE, "About to call WlxActivateUserShell(%#x, %ws, %ws, %#x)\n",
pTerm->Gina.pGinaContext, szDesktop,
pWS->LogonScripts, NULL));
DebugBreak();
}
#endif
ErrorMode = SetErrorMode( pTerm->ErrorMode );
StartStatus = pTerm->Gina.pWlxActivateUserShell(pTerm->Gina.pGinaContext,
szDesktop,
pWS->LogonScripts,
pNewEnvironment) ;
SetErrorMode( ErrorMode );
return StartStatus ;
}
F、使能SAS消息,然后激活winlogon桌面。
EnableSasMessages(NULL, pTerm);
SetActiveDesktop(pTerm, Desktop_Winlogon);
G、
if ( ( pTerm->WinlogonState == Winsta_Shutdown ) &&
( !g_Console ) )
{
DebugLog(( DEB_ERROR, "Winstation already shutdown?\n" ));
DebugBreak();
break;
}
Logoff(pTerm, WlxResult);
if (WlxResult == WLX_SAS_ACTION_LOGOFF)
{
pTerm->WinlogonState = Winsta_NoOne;
DebugLog((DEB_TRACE, "WlxResult was logoff, so beginning loop again\n"));
DebugLog((DEB_TRACE_STATE, "State set to %s\n", GetState(Winsta_NoOne)));
WlxResult = WLX_SAS_ACTION_NONE;
}
//
// Notify the GINA that the user is logged off
//
#if DBG
if (TEST_FLAG(GinaBreakFlags, BREAK_LOGOFF))
{
DebugLog((DEB_TRACE, "About to call WlxLogoff(%x)\n",
pTerm->Gina.pGinaContext));
DebugBreak();
}
#endif
pTerm->Gina.pWlxLogoff(pTerm->Gina.pGinaContext);
//
// For non-Console WinStations we exit WinLogon after
// one logon/logoff iteration.
//
if ( !g_Console ) {
break;
}
#ifndef _WIN64
//
// Don't start the NetDDE thread on sessions
//
if (g_Console) {
//
// start the application thread. Note: no win64 support
// for NetDDE (hooray!)
//
StartAppDesktopThread(pTerm);
}
#endif
//
// Toggle the winsta lock on and off. This clears the openlock, and
// sets the switchlock, allowing services to start, but no one can
// switch the active desktop.
//
UnlockWindowStation(pTerm->pWinStaWinlogon->hwinsta);
LockWindowStation(pTerm->pWinStaWinlogon->hwinsta);
#if DBG
if ((WlxResult == WLX_SAS_ACTION_NONE) ||
(WlxResult == WLX_SAS_ACTION_LOGON) ||
(WlxResult == WLX_SAS_ACTION_SHUTDOWN) ||
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_REBOOT) ||
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ||
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_SLEEP) ||
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_SLEEP2) ||
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_HIBERNATE) )
{
continue;
}
DebugLog((DEB_TRACE, "WlxResult not acceptible value: %d\n", WlxResult));
DebugLog((DEB_TRACE, "Resetting to WLX_SAS_ACTION_NONE\n"));
WlxResult = WLX_SAS_ACTION_NONE;
#endif
二、SAS窗口处理函数
三、重点介绍WlxDispatchTable