Winlogon、LSASS、Userinit

参考书籍:<<深入解析Windows操作系统>>

 

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家拍砖!

一、

Winlogon登录过程(\Windows\System32\Winlogon.exe)处理交互式用户登陆和注销,当安全注意序列(SAS,Secure Attention Sequence)组和键被按下时,Winlogon就会接到一个用户登陆请求。

一旦用户名和口令已经捕捉到了,就可以将它们关到本地安全认证服务器进程(\Windows\System32\Lsass.exe)进行认证。LSASS调用适当的认证包(实现为DLL的形式)以执行实际的验证操作,比如检查口令是否存储在活动目录或SAM中。

在成功完成了认证后,LSASS调用安全引用监视器中的一个函数(例如,NtCreateToken),以生成一个访问令牌,该对象包含了当前用户的安全轮廓。然后, winlogon利用此令牌创建该用户会话中的初始进程。这些初始进程被存储在注册表键HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon下的Userinit中。

Uerinit 执行该用户环境中的一些初始化工作(比如运行凳录脚本、应用组策略),然后在注册表中查找Shell值,并且创建一个进程来运行系统定义的外壳程序(默认是Explorer.exe).

然后,Userinit退出,这是为什么Explorer.exe 在进程树中没有父进程的原因------它的父进程已退出,而且正如前面解释的,tlist将所有父进程已经不在运行的进程调整到最左边。(看待这一现象的另一方法是:Explorer是Winlogon 的孙子进程)

 

 

二、

Smss就好像是另外一个用户模式进程,只是两个不同之处:

第一,windows将Smss看成是操作系统中可信的部分;

第二,Smss是一个原生(native)应用程序。因为它是一个可信的操作系统组件,所以,SMss可以执行一些其他很少有进程能执行的,比如创建安全令牌。因为它是一个原生

应用程序,所以,Smss不使用Windows API----它只使用核心执行体API,它些API合起来称为Windows原生API。Smss不使用Windows API,是国为当Smss激发起来的时候,

Windows 子系统尚未执行,实现上,Smss首要的任务之一是启动Windows子系统

然后,Smss调用配置管理器执行体子系统,来完成对注册表的初始化,充实注册表使其包含它所有的键。配置管理器的实现并不难:它知道那些核心的注册表存储在磁盘上的什么地方,并且它在HKLM\SYSTEM\CurretnControlSet\Control\hivelist键中记录了它要加载的储藏的路径。

 Smss的主线程执行以下的初始步骤:

1、创建一个LPC商品对象(\SmApiPort),以及两个线程来等待客户的请求(比如请求加载一个新的子系统或创建一个会话)。

2、为MS-DOS设备名称(比如COM1和LPT1)定义符号链接。

3、如果终端服务已被安装的话,则在对象管理器的名字空间中创建\Sessions目录(为了多会话支持)。


4、运行在HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute中定义的任何程序。在通常情况下,这个值包含了一条命令,它运行Autochk(Chkdsk的引导时刻版本)。

5、根据HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations和HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations2的指示,执行延迟的文件重命名和删除操作。

6、打开已知的DLL,为它们在对象管理器名字空间中的\Knowndlls目录下创建内存区对象。

这些被认为已知的DLL的列表,位于HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLS中,这些DLL所在的目录的路径被保存在此

键的Dlldirectory值中。

7、创建附加的页面文件。页面文件的配置信息保存在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\Paging Files 下。



8、初始注册表。配置管理器加载HKLM\SAM、HKLM\SECURITY和HKLM\SOFTWARE键的注册表储巢,以填充注册表的内容。

尽管HKLM\SYSTEM\CurrentControlSet\Control\hivelist给出了这些储文件在磁盘上的位置,但是,配置管理器被实现为:在\Windows\System32\Config下查找这些文件。

9、将HKLM\System\CurrentControlset\Session Manager\Environment中定义的系统环境变量建立起来。

10、加载Windows子系统的内核模式部分(Win32k.sys)。Smss在HKLM\SYSTEM\CurrentControlSet\Control\Session Manager下可以找到Win32k.sys和它要加载的其他组件的路径。Win32k.sys中的初始化代码利用已加载的视频驱动程序,将屏幕切换到默认轮廓定义的分辨率,所以,正是这个时候,屏幕从引导视频驱动程序所使用的VGA模式,

改变成系统所选择的默认分辨率。

 11、启动子系统进程,包括Csrss。

12、启动登陆进程(Winlogon)。

13、创建LPC端口(DbgSsApiPort和DbgUiApiPort),这些端口用于传递调试事件消息,创建相应线程在这些端口上监听。

在执行了这些初始化步骤后,Smss中的主线程进入无限等待,它等待Csrss和Winlogon的进程句柄。如果这两个中的一个非正常终止了,Smss就会让系统崩溃,因为

Windows依赖于这两个进程的存在。

然后Winlogon执行它的启动步骤,比如创建初始的窗口站和桌面对象。然后调用GINA。然后,winlogon创建服务控制管理器(SCM)进程(\windows\system32\Services.exe),而SCM进程又依次加载所有被标记为自动-启动斩服务和设备驱动程序,以及本地安全认证子系统(Lsass)进程(\Windows\System32\Lsass.exe)。

在SCM初始化了自动-启动服务和驱动程序,以及用户成功登陆到控制台后,SCM认为这次引导成功了。注册表中的最后已知的好控制集(如HKLM\SYSTEM\Select\LastKnownGood所指示)被更新与\CurrenntControlSet一致。

 

Winlogon激发了SCM以后,它便等待来自GINA的交互登录通知。它接收到一个登陆,并且验证该登陆确实有效以后,Winlogon从登陆用户的轮廓中加载注册表储存,并且将它们映射到HKCU。然后它设置该用户的环境变量(它们存在HKCU\Environmnet中),并且告诉那些在HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify中登记过的Winlogon通知包:当前发生过一次登陆过程

接下来Winlogon告诉GINA启动外壳程序。作为对该请求的响应,Msgina将HKLM\Software\Microsoft\Windows NT\CurretnVersion\WinLogon\Userinit中指定的一个或多个可执行程序激发起来。默认是Userinit.exe,它的执行步骤如下:

1、处理HKCU\Software\Policies\Microsoft\Windows\System\Scripts中的机器登陆脚本。

2、如果组策略指定了一个用户轮廓配额,则启动\Windows\System32\Proquota.exe,以强制应用当前用户的配额

3、将HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell中指定的用逗号分割的一个或多个外壳程序激发起来。如果此注册表值不存在,则Userinit.exe将

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell中指定的一个或多个外壳程序激发起来,该注册表值默认是Explorer.exe

然后winlogon通知已经登记的网络提供者,有一个用户登录过来了。Mircrosoft的网络提供者,多提供者转发器(Multiple Provider Router,\Windows\System32\Mpr.dll)

HKCU\Network和HKCU\Printers中存储的永久驱动字母和打印机映射关系分别恢复起来

三、登录(Logon)

交互式登录(相对于网络登录)是通过登录进程(Winlogon)、Lsass、一个或多个认证包,以及SAM活动目录之间的相互作用而发生的。认证包(authentication package)是执行认证检查的DLL。

当用户以交互方式登陆到一个域中时,Windows使用Kerberos作为认证包;当用户以交互方式登录到本地计算机时,或登录到windows 2000以前的可信域中时,或者当没有

域控制器可以访问时,windows使用MSV 1_0作为认证包。 

Winlogon是一个可信任的进程,它负责管理与安全有关的用户交互。它协调登录过程、在登录时启动用户的第一个进程、处理注销过程,以及管理其他各种与安全有关的操作,包括在登录时输入口令、改变口令、锁住工作站和解除工作站的锁定。

Winlogon进程确保这些与安全有关的操作对于任何其他的活动进程都是不可见的。例如,winlogon保证,在以上任何一个操作过程中,一个不可信的进程无法通过获得对桌面

的控制来得到对口令的访问。

 

除了支持可替换的GINA以外,Winlogon也可以加载那些额外的、用于执行二级认证所需要的网络提供者的DLL。这一能力使得多个网络

提供者可以在正常的登录过程中一次收集所有的标识和认证信息。一个登录到windows系统中的用户可能同时也在一台UNIX服务器上进行认证。然后,该用户

就能够直接从Windows机器上访问此UNIX服务器中的资源,而无需再要求额外的认证。这样的能力也被认为是“单点登录(Single sign-on)"的一种形式。

Winlogon初始化

在系统初始化过程中,在任何用户应用程序被激活起来以前,一旦系统准备好与用户进行交互了,Winlogon就执行下面的步骤来确保它控制整个工作站:

1、创建并打开一个交互式窗口站(例如,对象管理器名字空间中的\Windows\WindowStations\WinSta0)来代表键盘、鼠标和监视器。Winlogon为该窗口站创建一个安全描述符,它有且只有一个ACE,并且此ACE只包含了System SID。这一特殊的安全描述符确保了:除非得到Winlogon的明确许可,否则没有其他的进程可以访问此窗口站

 2、创建和打开两个桌面:一个应用程序桌面(\Windows\WinSta0\Default,也称为交互式桌面)和一个Winlogon桌面(\Windows\WinSta0\Winlogon,也称为安全桌面)。

Winlogon桌面上的安全性被建立起来:只有WInlogon才能访问此桌面。另一桌面既允许Winlogon,也允许用户访问它。这种安排意味着,不管什么时候,当WInlogon桌面是

活动桌面时,没有任何其他的进程可以访问与该桌面相关联的任何活动代码或数据。Windows使用此特性来保护与口令有关的安全操作,以及锁定桌面和解除锁定这样的操作。

//
// Windows object names
//

#define BASE_WINDOW_STATION_NAME      TEXT("WinSta")
#define WINDOW_STATION_NAME           TEXT("WinSta0")
#define APPLICATION_DESKTOP_NAME      TEXT("Default")
#define WINLOGON_DESKTOP_NAME         TEXT("Winlogon")
#define SCREENSAVER_DESKTOP_NAME      TEXT("Screen-saver")


 

 

3、在任何人登录到一台计算机上以前,可视的桌面正是Winlogon桌面。当有一个用户登录时,他一旦按下了CTRL_ALT_DEL,就从默认桌面切换到Winlogon桌面(这也解释了为什么当按下时,他的交互桌面上的所有窗口一下子消失了,然后,当他解除了WIndows Security 对话框时,这些窗口又回了)。因此,SAS总是带出一个由Winlogon控制的安全桌面。

4、与Lsass的LsaAuthenticationPort建立一个LPC连接。此连接将被用户在登录、注销和口令操作过程中交换信息,它是通过LsaRegisterLogonProcess而建立起来的。

然后Winlgon执行以下Windows操作,以建立起窗口环境。

所以在GINA的源码中有:

bool SecurityHelper::RegisterLogonProcess(const char* logonProcessName, HANDLE* phLsa) {
    *phLsa = 0;

    LSA_STRING name;
    if (!_newLsaString(&name, logonProcessName)) return false;

    LSA_OPERATIONAL_MODE unused;
    NTSTATUS status = LsaRegisterLogonProcess(&name, phLsa, &unused);

    _deleteLsaString(&name);

    if (status) {
        *phLsa = 0;
        LCF1(L"LsaRegisterLogonProcess failed: %d", LsaNtStatusToWinError(status));
        return false;
    }
    return true;
}


 

 5、它初始化一个窗口类数据结构,通过该窗口类数据结构将Winlogon窗口过程与它随后的窗口关联起来。

6、它登记SAS,将它与刚刚创建的窗口关联起来,从而保证:无论何时当用户按下SAS时,Winlogon的窗口过程就会被调用,这种办法可防止特洛伊木马程序在用户按下SAS时获得对屏幕的控制。

关于代码请参考我的文章<<winlogon源码分析>>,其中有文字“SASInit做了以下几件事”

7、它登记该窗口,因而当用户注销登录,或者屏幕保护程序超时时,与该窗口关联的窗口过程能被调用到。Windows子系统会进行检查,以检验此“请求获得通知”的进程是

Winlogon进程。

 

SAS是如何实现的

SAS是安全的,因为不有应用程序能截取Ctrl+Alt+Del组合键,也无法阻止Windlogon接收此键击组合。Winlogon使用一个文档化的Windows API,即Registerhotkey来

保留Ctrl+Alt+Del键组合,因此,无论何时当Windows输入系统看到此组合时,它给Winlogon创建的一个窗口发送一个特殊的消息,让它接收这样的通知。凡是被映射到一个

已登记热键的键击都只能发送到登记该键击的那个进程,而不再发送到任何其他进程,而且,只有登记热键的好运个线程才可以注销该热键(通过UnregisterHotKey API),

因此,特洛伊木马应用程序不可能注销Winlogon对于SAS的所有权。

Windows函数SetWindowsHook允许一个应用程序安装一个钩子过程,因而每次当一个键被按下时(甚至在热键被处理以前),钩子过程会被调用;

它也允许该钩子将用户的键击动作抵制住。然而,Windows的热键处理代码包含了针对Ctrl+Atl+Del的专门处理,对于此键击序列,它禁止任何钩子,所以,

此键击序列不会被截取。而且,如果交互桌面被锁住的话,则只有Winlogon拥有的热键才被处理

 

 

在初始化过程中一旦Winlogon桌面被创建,它就成为活动的桌面。当Winlogon桌面是活动桌面时,它总是被锁住的。Winlogon解除其桌面的锁定,仅仅是切换到应用程序桌面,

或者屏幕保护程序的桌面(只有winlogon进程才可以锁定一个桌面,或是解除桌面的锁定)。

 

用户登录步骤

 当用户按下SAS(CTRL+ALT+DEL)时,登录便开始了,当SAS被按下以后,Winlogon调用Gina来获得一个用户名和口令。Winlogon也为该用户创建一个唯一的登录SID

 这是它分配给此桌面实例(键盘、屏幕和鼠标)的SID。Winlogon在LsaLogonUser调用中将此SID传递给Lsass.

 

以下的代码是获得SID。

  HANDLE hLsa;
    if (!SecurityHelper::RegisterLogonProcess(LOGON_PROCESS_NAME, &hLsa)) {
        return FALSE;
    }

    *ppNewGina = new Gina(pWinLogon, hLsa);

调用API: LsaRegisterLogonProcess得到SID。

bool SecurityHelper::RegisterLogonProcess(const char* logonProcessName, HANDLE* phLsa) {
    *phLsa = 0;

    LSA_STRING name;
    if (!_newLsaString(&name, logonProcessName)) return false;

    LSA_OPERATIONAL_MODE unused;
    NTSTATUS status = LsaRegisterLogonProcess(&name, phLsa, &unused);

    _deleteLsaString(&name);

    if (status) {
        *phLsa = 0;
        LCF1(L"LsaRegisterLogonProcess failed: %d", LsaNtStatusToWinError(status));
        return false;
    }
    return true;
}


 

 

然后将此SID保存起来.

Gina::Gina(IWinLogon* pWinLogon, HANDLE hLsa)
  : _pWinLogon(pWinLogon), _hLsa(hLsa), _hToken(0), _profilePath(0), _pStatusWindow(0) {


 

     if (!SecurityHelper::CallLsaLogonUser(_hLsa,
                                                strDomain, strUserName, strPassWord,
                                                Interactive,
                                                pAuthenticationId, phToken,
                                                &pProfile, &win32Error)) {


以上代码是在调用LsaLogonUser时使用SID,传递给Lsass

 // LsaLogonUser - the function from hell
    NTSTATUS status = LsaLogonUser(
        hLsa,
        &logonProcessName,  // we use our logon process name for the "origin name"
        logonType,
        LOGON32_PROVIDER_DEFAULT, // we use MSV1_0 or Kerb, whichever is supported
        pLogonRequest,
        cbLogonRequest,
        0,                  // we do not add any group SIDs
        &sourceContext,
        (void**)&pProfile,  // caller must free this via LsaFreeReturnBuffer 
        &cbProfile,
        pLogonSessionId,
        phToken,
        "aLimits,       // we ignore this, but must pass in anyway
        &substatus);


 

 如果该用户成功登录,则此SID将被包含在登录进程的令牌中------这一步骤保护了对桌面的访问。例如,在另一个系统上登录到同一个账户下将不能对第一台

机器的桌面进行写操作,因为第二个登录不是在第一个登录的桌面令牌中。

当用户名和口令被输入时,Winlogon通过调用Lsass的函数LsaLookupAuthenticationPackage来获得一个指向论证包的句柄,在注册表的HKLM\SYSTEM\CurrentControlSet\Lsa下面列出了系统中的论证包。Winlogon通过LsaLogonuser将登录信息传递给论证包。一旦论证包论证了一个用户,Winlogon便继续

该用户的登录过程。如果没有一个论证包指示这是一次成功的登录,则登录以失败告终。

对于交互式登录,Windows使用两个标准的论证包:Kerberos和MSV1_0。在一个单独的Windows系统上默认的论证包是MSV1_0(\Windows\System32\Msv1_0.dll),

这是一个实现了“LAN Manager2"协议的论证包。Lsass也在域成员计算机上使用MSV1_0来认证Windows2000之前的域和未能找到域控制器来进行论证的计算机(与网络

断开连接的笔计本也归入后一类)。

Kerberos认证包(\Windows\System32\Keberos.dll)被用在Windows域的成员计算机上。Winodws的Keberos认证包,连同域控制器上运行的Kerberos服务,两者联合起来

支持Kerberos协议版本5和修订版6。该协议是Internet RFC 1510为基础的。

MSV 1_0认证包接受用户名和经过散列计算后的口令值,并且向本地的SAM发送一个请求,以获取有关账户的信息,其中包括口令、用户所属的组,以及该账户的任何限制。

MSV1_0首先检查该账户的限制,比如允许访问的小时时间或类型。如果该用户由于SAM数据库中的限制而不能登录,那么这次登录失败,MSV1_0向LSA返回一个失败状态。

 

 MSV1_0_INTERACTIVE_LOGON* pLogonRequest =
        _allocLogonRequest(domain, user, pass, &cbLogonRequest);
    if (!pLogonRequest) {
        win32Error = ERROR_NOT_ENOUGH_MEMORY;
        goto cleanup;   
    }

    if (!_newLsaString(&logonProcessName, LOGON_PROCESS_NAME)) {
        win32Error = ERROR_NOT_ENOUGH_MEMORY;
        goto cleanup;
    }

    // LsaLogonUser - the function from hell
    NTSTATUS status = LsaLogonUser(
        hLsa,
        &logonProcessName,  // we use our logon process name for the "origin name"
        logonType,
        LOGON32_PROVIDER_DEFAULT, // we use MSV1_0 or Kerb, whichever is supported
        pLogonRequest,
        cbLogonRequest,
        0,                  // we do not add any group SIDs
        &sourceContext,
        (void**)&pProfile,  // caller must free this via LsaFreeReturnBuffer 
        &cbProfile,
        pLogonSessionId,
        phToken,
        "aLimits,       // we ignore this, but must pass in anyway
        &substatus);


 生成一个请求。

static MSV1_0_INTERACTIVE_LOGON* _allocLogonRequest(
    const wchar_t* domain,
    const wchar_t* user,
    const wchar_t* pass,
    DWORD* pcbRequest) {

    const DWORD cbHeader = sizeof(MSV1_0_INTERACTIVE_LOGON);
    const DWORD cbDom    = _stringLenInBytes(domain);
    const DWORD cbUser   = _stringLenInBytes(user);
    const DWORD cbPass   = _stringLenInBytes(pass);

    // sanity check string lengths
    if (cbDom > USHRT_MAX || cbUser > USHRT_MAX || cbPass > USHRT_MAX) {
        LCF(L"Input string was too long");
        return 0;
    }

    *pcbRequest = cbHeader + cbDom + cbUser + cbPass;

    MSV1_0_INTERACTIVE_LOGON* pRequest = (MSV1_0_INTERACTIVE_LOGON*)new char[*pcbRequest];
    if (!pRequest) {
        LOOM;
        return 0;
    }

    pRequest->MessageType = MsV1_0InteractiveLogon;

    char* p = (char*)(pRequest + 1); // point past MSV1_0_INTERACTIVE_LOGON header

    wchar_t* pDom  = (wchar_t*)(p);
    wchar_t* pUser = (wchar_t*)(p + cbDom);
    wchar_t* pPass = (wchar_t*)(p + cbDom + cbUser);

    CopyMemory(pDom,  domain, cbDom);
    CopyMemory(pUser, user,   cbUser);
    CopyMemory(pPass, pass,   cbPass);
    
    _initUnicodeString(&pRequest->LogonDomainName, pDom,  (USHORT)cbDom);
    _initUnicodeString(&pRequest->UserName,        pUser, (USHORT)cbUser);
    _initUnicodeString(&pRequest->Password,        pPass, (USHORT)cbPass);

    return pRequest;
}


 

 然后,MSV1_0将用户和散列后的口令值,分别与SAM中存储的用户和散列后的口令值进行比较。如果这是一个缓存的域登录,则MSV1_0使用Lsaaa 的有关函数来访问缓存

的信息;通过Lsass的这些函数,可以从LSA数据库中获得这些“秘密”,或将“秘密”存储到LSA数据库中。如果用户输入的信息与SAM中的信息相符,则MSV1_0为该登录用户会话生成一个LUID,并且通过调用Lsaaa来创建此登录会话;它也将此惟一标识与该会话关联起来,而且,在调用Lsass 时传递必要的信息以便最终为该用户创建一个

访问令牌(一个访问令牌包含用户SID、组SID和分配的特权)

LSASS函数:

定义在“C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include”的NTSecAPI.h中

LsaLogonUser

LsaFreeReturnBuffer

 LsaDeregisterLogonProcess

 LsaRegisterLogonProcess

LsaNtStatusToWinError

 LsaLookupAuthenticationPackage

 LsaCallAuthenticationPackage

 MSV1_0 并没有将用户完整的口令散列存在注册表中,因为这将使得任何能通过物理方式访问该系统的人很容易就能危及该用户的域账户,并且访问到该用户的加密文件,

以及任何该用户有权访问的网络资源。

相反地,MSV1_0缓存了此散列值的一半。这缓存的一半散列值,对于验证用户口令的正确性来说,已经够了。但想要访问EFS密钥,或者证明自己是一个域中的用户,

则仍然不够,因这这些操作需要整个散列值。

如果MSV 1_0需要使用一个远程系统来认证身份,比如当用户登录到一个Windows 2000之前的可信域时,MSV 1_0使用Netlogon服务来跟远程系统上的Netlogon实例

进行通信。远程系统上的Netlogon与该系统上的MSV1_0 认证包进行交互,然后将认证结果送回到当前正在执行登录的系统上。

Kerberos 认证的基本控制流与MSV1 _0的控制流相同。然而,在绝大多数情况下,域登录是在成员工作站或成员服务器上执行的(而不是在域控制器上执行的),因此,

在认证过程中,认证包必须进行网络通信。Kerberos认证包的做法是,通过Kerberos TCP/IP端口(端口88)与域控制器上的Kerberos服务进行通信。

Kerberos密钥分发中心(Kerberos Key Distribution Center)服务( \Windows\system32\Kdcsvc.dll)实现了Kerberos认证协议,它运行在域控制器上的LSASS进程中。

Kdcsvc利用活动目录服务器\Windows\System32\Ntdsa.dll,通过活动目录的用户账户对象来验证散列的用户名和口令信息以后,向LSASS返回域凭证(domain credential),

而LSASS又通过网络的,向当前正在执行登录的系统返回认证的结果和用户的域登录凭证。

 

一次登录已经通过认证以后,LSASS在本地策略数据库中查找该用户有哪些允许的登录方式,包括交互方式、网络方式、批方式或服务方式。

如果当前所请求的登录不符合数据库中允许的访问方式,则此次登录被终止。LSASS通过清除其所有的数据结构方式来删除新建的登录会话,然后向

Winlogon返回失败,而Winlogon又依次向用户显示一个适当的消息。

如果当前请求的登录是允许的,则LSASS加入一些额外的安全ID(如Evenryone,Interactive之类的)。

然后它在策略数据库中检查该用户所有ID的特权,并且将这些已授予的特权加入到用户的访问令牌中。

 

 

下面介绍一种方法:列出活动的登录会话

对于一个给定的登录会话LUID,只要存在至少一个令牌,Windows就会认为登录会话是活动的

你可以使用www.sysinternals.com的LogonSessions工具来列出活动的登录会话,该工具用到了LsaEnumerateLogonSessions函数。

 

下载后的LogonSession.exe为:

如果直接运行,那么会一闪而过。解决方法是把此EXE放到system32目录下,然后“运行”-》CMD,然后运行logonsessions,得到结果:

C:\Documents and Settings\Administrator>logonsessions

Logonsesions v1.21
Copyright (C) 2004-2010 Bryce Cogswell and Mark Russinovich
Sysinternals - wwww.sysinternals.com


[0] Logon session 00000000:000003e7:
    User name:    WORKGROUP\PC2009110322EPN$
    Auth package: NTLM
    Logon type:   (none)
    Session:      0
    Sid:          S-1-5-18
    Logon time:   2012-12-07 08:52:33
    Logon server:
    DNS Domain:
    UPN:

[1] Logon session 00000000:00013960:
    User name:
    Auth package: NTLM
    Logon type:   (none)
    Session:      0
    Sid:          (none)
    Logon time:   2012-12-07 08:52:33
    Logon server:
    DNS Domain:
    UPN:

[2] Logon session 00000000:000003e4:
    User name:    NT AUTHORITY\NETWORK SERVICE
    Auth package: Negotiate
    Logon type:   Service
    Session:      0
    Sid:          S-1-5-20
    Logon time:   2012-12-07 08:52:34
    Logon server:
    DNS Domain:
    UPN:

[3] Logon session 00000000:0001d70f:
    User name:    NT AUTHORITY\ANONYMOUS LOGON
    Auth package: NTLM
    Logon type:   Network
    Session:      0
    Sid:          S-1-5-7
    Logon time:   2012-12-07 08:52:50
    Logon server:
    DNS Domain:
    UPN:

[4] Logon session 00000000:0001d941:
    User name:    PC2009110322EPN\Administrator
    Auth package: NTLM
    Logon type:   Interactive
    Session:      0
    Sid:          S-1-5-21-1123561945-117609710-1417001333-500
    Logon time:   2012-12-07 08:52:50
    Logon server: PC2009110322EPN
    DNS Domain:
    UPN:

[5] Logon session 00000000:000003e5:
    User name:    NT AUTHORITY\LOCAL SERVICE
    Auth package: Negotiate
    Logon type:   Service
    Session:      0
    Sid:          S-1-5-19
    Logon time:   2012-12-07 08:53:26
    Logon server:
    DNS Domain:
    UPN:

注意:在以上输出的登录会话2中,Negotiate认证包是通过Kerberos还是通过NTLM来进行认证,这取决于哪一个认证包对于这次认证请求更为合适。

会的LUID被显示在每一个会话输出块中的“Logon Session"这一行上,利用Handle工具,你可以找到代表一个特定登录会话的令牌。例如,要想找到上述例子中显示的登录会话8的令牌,你可以输入以下命令:

C:\>handle -a 79da73

43c:Token  MARKLAP\Administrator:79da73

然后,Winlogon查看注册表中HKLM\SOFTWARE\Microsoft\Winodws NT\Current Version\Winlogon\Userinit的值,并且无论该字符串的值是什么,它都创建

一个进程来运行该字符串值(该值可能是几个用逗号来隔开的EXE)默认值是Userinit.exe,它加载该用户的轮廓

然后,创建一个进行来运行HKCU\SOFTWARE\Microft\Windows NT\Current Version\Winlogon\Shell中的字符串值(如果该值存在的话)。

在默认情况下,该值不存在。

二、windows下获取logon session信息(转)

什么是LSA?什么是session?MSDN中的描述如下:

Local Security Authority

     (LSA) A protected subsystem that authenticates and logs users onto the local system. LSA also maintains information about all aspects of local security on a system, collectively known as the Local Security Policy of the system.    

(LSA) 一个判断用户登陆本系统时候的保护子系统。LSA也包含有关本系统中安全的所有方面,统称为本地安全策略的系统。
logon session

     A logon session begins whenever a user logs on to a computer. All processes in a logon session have the same primary access token. The access token contains information about the security context of the logon session, including the user's SID, the logon identifier, and the logon SID. 

   当用户登陆计算机时,一个对应的logon session就开始了。一个logon session可以拥有多个进程,计算机上运行着的所有进程都属于一个唯一的session。怎么获取这些session和进程的相关信息呢?

  LsaEnumerateLogonSessions函数可以获取已经存在的logon session identifiers (LUIDs) 和session的总数。

NTSTATUS NTAPI LsaEnumerateLogonSessions(
       PULONG LogonSessionCount,
       PLUID* LogonSessionList
   );


当LogonSessionList不再需要时,需要调用LSAFreeReturnBuffer函数来释放所占用的内存

     看看刚才获取的LogonSessionList,数据类型为LUID。

typedef struct _LUID {
       DWORD LowPart;
       LONG HighPart;
   } LUID, *PLUID;


为了通过LUID来获取详细的logon session信息,需要调用函数LsaGetLogonSessionData,调用者必须是拥有该session或者是本地的系统管理员

NTSTATUS NTAPI LsaGetLogonSessionData(
       PLUID LogonId,
       PSECURITY_LOGON_SESSION_DATA* ppLogonSessionData
   );


LsaGetLogonSessionData函数返回一个PSECURITY_LOGON_SESSION_DATA结构体。

typedef struct _SECURITY_LOGON_SESSION_DATA {
       ULONG Size;
       LUID LogonId;
       LSA_UNICODE_STRING   UserName;
       LSA_UNICODE_STRING   LogonDomain;
       LSA_UNICODE_STRING   AuthenticationPackage;
       ULONG LogonType;
       ULONG Session;
       PSID Sid;
       LARGE_INTEGER LogonTime;
       LSA_UNICODE_STRING   LogonServer;
       LSA_UNICODE_STRING   DnsDomainName;
       LSA_UNICODE_STRING Upn;
   } SECURITY_LOGON_SESSION_DATA, *PSECURITY_LOGON_SESSION_DATA;


其中包含了登陆标识(LogonId)、登陆的账号(UserName)、域(LogonDomain)、认证方式(AuthenticationPackage)、登陆类型(LogonType)、会话ID(Session)、用户的Sid(Sid)、用户登陆时间(LogonTime)等信息。

     登陆类型(LogonType)是个枚举类型。

typedef enum _SECURITY_LOGON_TYPE {
       Interactive = 2,    // Interactively logged on (locally or remotely)
       Network,             // Accessing system via network
       Batch,                 // Started via a batch queue
       Service,              // Service started by service   controller
       Proxy,                 // Proxy logon
       Unlock,               // Unlock workstation
       NetworkCleartext,   // Network logon with cleartext credentials
       NewCredentials,     // Clone caller, new default credentials
       RemoteInteractive,   // Remote, yet interactive. Terminal server
       CachedInteractive,   // Try cached credentials without hitting the   net.
       CachedRemoteInteractive, // Same as RemoteInteractive, this is used internally for auditing purpose
       CachedUnlock         // Cached Unlock workstation
   } SECURITY_LOGON_TYPE, *PSECURITY_LOGON_TYPE;


用户的Sid(Sid)可以用ConvertSidToStringSid来转换成常见的SID格式字符串。

BOOL ConvertSidToStringSid(
       PSID Sid,
       LPTSTR* StringSid
   );


这样,所有logon session的信息就获取到了。

     更进一步的,用EnumProcesses函数枚举进程ID,OpenProcess获取每一个进程的句柄。在分别通过OpenProcessToken和GetTokenInformation打开并获取进程的访问令牌信息。

BOOL OpenProcessToken(
       HANDLE ProcessHandle,
       DWORD DesiredAccess,
       PHANDLE TokenHandle
   );

   BOOL GetTokenInformation(
       HANDLE TokenHandle,
       TOKEN_INFORMATION_CLASS   TokenInformationClass,
       LPVOID TokenInformation,
       DWORD   TokenInformationLength,
       PDWORD ReturnLength
   );


TokenInformationClass是个枚举类型,用来指明要获取的信息类型,这里用TokenStatistics即可。获取的信息在TokenInformation中,数据类型为TOKEN_STATISTICS的结构体。

typedef struct _TOKEN_STATISTICS {
       LUID TokenId;
       LUID AuthenticationId;
       LARGE_INTEGER   ExpirationTime;
       TOKEN_TYPE TokenType;
       SECURITY_IMPERSONATION_LEVEL   ImpersonationLevel;
       DWORD DynamicCharged;
       DWORD DynamicAvailable;
       DWORD GroupCount;
       DWORD PrivilegeCount;
       LUID ModifiedId;
   } TOKEN_STATISTICS, *PTOKEN_STATISTICS;


  其中LUID AuthenticationId如果和前面logon session的LUID一致,说明该进程的拥有者是相应的logon session。

     通过类似的方法,能获取很多有用的信息。下面的程序是用这些API写的一个windows下获取logon session信息,并列举属于该session的进程。

 

 

 

 

 

 

 

你可能感兴趣的:(登陆/GINA)